From 958ed4f3d977b08465915e475e11aaab3d2dc574 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sun, 1 Oct 2017 21:07:30 +0200 Subject: [PATCH 01/36] core/state: rework dirty handling to avoid quadratic overhead --- core/state/dump.go | 2 +- core/state/journal.go | 75 ++++++++++++++++++++++++++++++++++---- core/state/state_object.go | 51 +++++++------------------- core/state/statedb.go | 52 +++++++++++++------------- core/state/statedb_test.go | 5 ++- tests/state_test.go | 6 --- 6 files changed, 112 insertions(+), 79 deletions(-) diff --git a/core/state/dump.go b/core/state/dump.go index 46e612850a..072dbbf053 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -53,7 +53,7 @@ func (self *StateDB) RawDump() Dump { panic(err) } - obj := newObject(nil, common.BytesToAddress(addr), data, nil) + obj := newObject(nil, common.BytesToAddress(addr), data) account := DumpAccount{ Balance: data.Balance.String(), Nonce: data.Nonce, diff --git a/core/state/journal.go b/core/state/journal.go index a89bb3d13a..b00a462245 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -24,9 +24,40 @@ import ( type journalEntry interface { undo(*StateDB) + getAccount() *common.Address } -type journal []journalEntry +type journal struct { + entries []journalEntry + dirtyOverrides []common.Address +} + +func (j *journal) append(entry journalEntry) { + j.entries = append(j.entries, entry) +} + +func (j *journal) flatten() map[common.Address]struct{} { + + dirtyObjects := make(map[common.Address]struct{}) + for _, journalEntry := range j.entries { + if addr := journalEntry.getAccount(); addr != nil { + dirtyObjects[*addr] = struct{}{} + } + } + for _, addr := range j.dirtyOverrides { + dirtyObjects[addr] = struct{}{} + } + return dirtyObjects +} + +// Length returns the number of journal entries in the journal +func (j *journal) Length() int { + return len(j.entries) +} + +func (j *journal) dirtyOverride(address common.Address) { + j.dirtyOverrides = append(j.dirtyOverrides, address) +} type ( // Changes to the account trie. @@ -82,10 +113,18 @@ func (ch createObjectChange) undo(s *StateDB) { delete(s.stateObjectsDirty, *ch.account) } +func (ch createObjectChange) getAccount() *common.Address { + return ch.account +} + func (ch resetObjectChange) undo(s *StateDB) { s.setStateObject(ch.prev) } +func (ch resetObjectChange) getAccount() *common.Address { + return nil +} + func (ch suicideChange) undo(s *StateDB) { obj := s.getStateObject(*ch.account) if obj != nil { @@ -93,37 +132,52 @@ func (ch suicideChange) undo(s *StateDB) { obj.setBalance(ch.prevbalance) } } +func (ch suicideChange) getAccount() *common.Address { + return ch.account +} var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") func (ch touchChange) undo(s *StateDB) { - if !ch.prev && *ch.account != ripemd { - s.getStateObject(*ch.account).touched = ch.prev - if !ch.prevDirty { - delete(s.stateObjectsDirty, *ch.account) - } - } +} +func (ch touchChange) getAccount() *common.Address { + return ch.account } func (ch balanceChange) undo(s *StateDB) { s.getStateObject(*ch.account).setBalance(ch.prev) } +func (ch balanceChange) getAccount() *common.Address { + return ch.account +} func (ch nonceChange) undo(s *StateDB) { s.getStateObject(*ch.account).setNonce(ch.prev) } +func (ch nonceChange) getAccount() *common.Address { + return ch.account +} func (ch codeChange) undo(s *StateDB) { s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) } +func (ch codeChange) getAccount() *common.Address { + return ch.account +} func (ch storageChange) undo(s *StateDB) { s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) } +func (ch storageChange) getAccount() *common.Address { + return ch.account +} func (ch refundChange) undo(s *StateDB) { s.refund = ch.prev } +func (ch refundChange) getAccount() *common.Address { + return nil +} func (ch addLogChange) undo(s *StateDB) { logs := s.logs[ch.txhash] @@ -134,7 +188,14 @@ func (ch addLogChange) undo(s *StateDB) { } s.logSize-- } +func (ch addLogChange) getAccount() *common.Address { + return nil +} func (ch addPreimageChange) undo(s *StateDB) { delete(s.preimages, ch.hash) } + +func (ch addPreimageChange) getAccount() *common.Address { + return nil +} diff --git a/core/state/state_object.go b/core/state/state_object.go index b2112bfaec..523bb7150c 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -85,9 +85,7 @@ type stateObject struct { // during the "update" phase of the state transition. dirtyCode bool // true if the code was updated suicided bool - touched bool deleted bool - onDirty func(addr common.Address) // Callback method to mark a state object newly dirty } // empty returns whether the account is considered empty. @@ -105,7 +103,7 @@ type Account struct { } // newObject creates a state object. -func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *stateObject { +func newObject(db *StateDB, address common.Address, data Account) *stateObject { if data.Balance == nil { data.Balance = new(big.Int) } @@ -119,7 +117,6 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), - onDirty: onDirty, } } @@ -137,23 +134,17 @@ func (self *stateObject) setError(err error) { func (self *stateObject) markSuicided() { self.suicided = true - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } func (c *stateObject) touch() { - c.db.journal = append(c.db.journal, touchChange{ - account: &c.address, - prev: c.touched, - prevDirty: c.onDirty == nil, + c.db.journal.append(touchChange{ + account: &c.address, }) - if c.onDirty != nil { - c.onDirty(c.Address()) - c.onDirty = nil + if c.address == ripemd { + //Explicitly put it in the dirty-cache, which is otherwise + // generated from flattened journals + c.db.journal.dirtyOverride(c.address) } - c.touched = true } func (c *stateObject) getTrie(db Database) Trie { @@ -195,7 +186,7 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash { // SetState updates a value in account storage. func (self *stateObject) SetState(db Database, key, value common.Hash) { - self.db.journal = append(self.db.journal, storageChange{ + self.db.journal.append(storageChange{ account: &self.address, key: key, prevalue: self.GetState(db, key), @@ -207,10 +198,6 @@ func (self *stateObject) setState(key, value common.Hash) { self.cachedStorage[key] = value self.dirtyStorage[key] = value - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } // updateTrie writes cached storage modifications into the object's storage trie. @@ -274,7 +261,7 @@ func (c *stateObject) SubBalance(amount *big.Int) { } func (self *stateObject) SetBalance(amount *big.Int) { - self.db.journal = append(self.db.journal, balanceChange{ + self.db.journal.append(balanceChange{ account: &self.address, prev: new(big.Int).Set(self.data.Balance), }) @@ -283,17 +270,13 @@ func (self *stateObject) SetBalance(amount *big.Int) { func (self *stateObject) setBalance(amount *big.Int) { self.data.Balance = amount - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } // Return the gas back to the origin. Used by the Virtual machine or Closures func (c *stateObject) ReturnGas(gas *big.Int) {} -func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject { - stateObject := newObject(db, self.address, self.data, onDirty) +func (self *stateObject) deepCopy(db *StateDB) *stateObject { + stateObject := newObject(db, self.address, self.data) if self.trie != nil { stateObject.trie = db.db.CopyTrie(self.trie) } @@ -333,7 +316,7 @@ func (self *stateObject) Code(db Database) []byte { func (self *stateObject) SetCode(codeHash common.Hash, code []byte) { prevcode := self.Code(self.db.db) - self.db.journal = append(self.db.journal, codeChange{ + self.db.journal.append(codeChange{ account: &self.address, prevhash: self.CodeHash(), prevcode: prevcode, @@ -345,14 +328,10 @@ func (self *stateObject) setCode(codeHash common.Hash, code []byte) { self.code = code self.data.CodeHash = codeHash[:] self.dirtyCode = true - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } func (self *stateObject) SetNonce(nonce uint64) { - self.db.journal = append(self.db.journal, nonceChange{ + self.db.journal.append(nonceChange{ account: &self.address, prev: self.data.Nonce, }) @@ -361,10 +340,6 @@ func (self *stateObject) SetNonce(nonce uint64) { func (self *stateObject) setNonce(nonce uint64) { self.data.Nonce = nonce - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } func (self *stateObject) CodeHash() []byte { diff --git a/core/state/statedb.go b/core/state/statedb.go index bd67e789d5..7a88b3b16c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -131,7 +131,7 @@ func (self *StateDB) Reset(root common.Hash) error { } func (self *StateDB) AddLog(log *types.Log) { - self.journal = append(self.journal, addLogChange{txhash: self.thash}) + self.journal.append(addLogChange{txhash: self.thash}) log.TxHash = self.thash log.BlockHash = self.bhash @@ -156,7 +156,7 @@ func (self *StateDB) Logs() []*types.Log { // AddPreimage records a SHA3 preimage seen by the VM. func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) { if _, ok := self.preimages[hash]; !ok { - self.journal = append(self.journal, addPreimageChange{hash: hash}) + self.journal.append(addPreimageChange{hash: hash}) pi := make([]byte, len(preimage)) copy(pi, preimage) self.preimages[hash] = pi @@ -169,7 +169,7 @@ func (self *StateDB) Preimages() map[common.Hash][]byte { } func (self *StateDB) AddRefund(gas uint64) { - self.journal = append(self.journal, refundChange{prev: self.refund}) + self.journal.append(refundChange{prev: self.refund}) self.refund += gas } @@ -255,7 +255,7 @@ func (self *StateDB) StorageTrie(addr common.Address) Trie { if stateObject == nil { return nil } - cpy := stateObject.deepCopy(self, nil) + cpy := stateObject.deepCopy(self) return cpy.updateTrie(self.db) } @@ -325,7 +325,7 @@ func (self *StateDB) Suicide(addr common.Address) bool { if stateObject == nil { return false } - self.journal = append(self.journal, suicideChange{ + self.journal.append(suicideChange{ account: &addr, prev: stateObject.suicided, prevbalance: new(big.Int).Set(stateObject.Balance()), @@ -379,7 +379,7 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje return nil } // Insert into the live set. - obj := newObject(self, addr, data, self.MarkStateObjectDirty) + obj := newObject(self, addr, data) self.setStateObject(obj) return obj } @@ -397,22 +397,16 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { return stateObject } -// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly -// state object cache iteration to find a handful of modified ones. -func (self *StateDB) MarkStateObjectDirty(addr common.Address) { - self.stateObjectsDirty[addr] = struct{}{} -} - // createObject creates a new state object. If there is an existing account with // the given address, it is overwritten and returned as the second return value. func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { prev = self.getStateObject(addr) - newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty) + newobj = newObject(self, addr, Account{}) newobj.setNonce(0) // sets the object to dirty if prev == nil { - self.journal = append(self.journal, createObjectChange{account: &addr}) + self.journal.append(createObjectChange{account: &addr}) } else { - self.journal = append(self.journal, resetObjectChange{prev: prev}) + self.journal.append(resetObjectChange{prev: prev}) } self.setStateObject(newobj) return newobj, prev @@ -462,20 +456,22 @@ func (self *StateDB) Copy() *StateDB { self.lock.Lock() defer self.lock.Unlock() + dirtyObjects := self.journal.flatten() + // Copy all the basic fields, initialize the memory ones state := &StateDB{ db: self.db, trie: self.db.CopyTrie(self.trie), - stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), + stateObjects: make(map[common.Address]*stateObject, len(dirtyObjects)), + stateObjectsDirty: make(map[common.Address]struct{}, len(dirtyObjects)), refund: self.refund, logs: make(map[common.Hash][]*types.Log, len(self.logs)), logSize: self.logSize, preimages: make(map[common.Hash][]byte), } // Copy the dirty states, logs, and preimages - for addr := range self.stateObjectsDirty { - state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) + for addr := range dirtyObjects { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) state.stateObjectsDirty[addr] = struct{}{} } for hash, logs := range self.logs { @@ -492,7 +488,7 @@ func (self *StateDB) Copy() *StateDB { func (self *StateDB) Snapshot() int { id := self.nextRevisionId self.nextRevisionId++ - self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)}) + self.validRevisions = append(self.validRevisions, revision{id, self.journal.Length()}) return id } @@ -508,10 +504,10 @@ func (self *StateDB) RevertToSnapshot(revid int) { snapshot := self.validRevisions[idx].journalIndex // Replay the journal to undo changes. - for i := len(self.journal) - 1; i >= snapshot; i-- { - self.journal[i].undo(self) + for i := self.journal.Length() - 1; i >= snapshot; i-- { + self.journal.entries[i].undo(self) } - self.journal = self.journal[:snapshot] + self.journal.entries = self.journal.entries[:snapshot] // Remove invalidated snapshots from the stack. self.validRevisions = self.validRevisions[:idx] @@ -525,7 +521,8 @@ func (self *StateDB) GetRefund() uint64 { // Finalise finalises the state by removing the self destructed objects // and clears the journal as well as the refunds. func (s *StateDB) Finalise(deleteEmptyObjects bool) { - for addr := range s.stateObjectsDirty { + + for addr, v := range s.journal.flatten() { stateObject := s.stateObjects[addr] if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { s.deleteStateObject(stateObject) @@ -533,6 +530,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { stateObject.updateRoot(s.db) s.updateStateObject(stateObject) } + s.stateObjectsDirty[addr] = v } // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() @@ -576,7 +574,7 @@ func (s *StateDB) DeleteSuicides() { } func (s *StateDB) clearJournalAndRefund() { - s.journal = nil + s.journal = journal{} s.validRevisions = s.validRevisions[:0] s.refund = 0 } @@ -585,6 +583,10 @@ func (s *StateDB) clearJournalAndRefund() { func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() + for addr, v := range s.journal.flatten() { + s.stateObjectsDirty[addr] = v + } + // Commit objects to the trie. for addr, stateObject := range s.stateObjects { _, isDirty := s.stateObjectsDirty[addr] diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index d9e3d9b797..05bc0499b8 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -413,11 +413,12 @@ func (s *StateSuite) TestTouchDelete(c *check.C) { snapshot := s.state.Snapshot() s.state.AddBalance(common.Address{}, new(big.Int)) - if len(s.state.stateObjectsDirty) != 1 { + + if len(s.state.journal.flatten()) != 1 { c.Fatal("expected one dirty state object") } s.state.RevertToSnapshot(snapshot) - if len(s.state.stateObjectsDirty) != 0 { + if len(s.state.journal.flatten()) != 0 { c.Fatal("expected no dirty state object") } } diff --git a/tests/state_test.go b/tests/state_test.go index 9ca5f18303..3fd3ce43ab 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -36,14 +36,8 @@ func TestState(t *testing.T) { st.skipLoad(`^stTransactionTest/zeroSigTransa[^/]*\.json`) // EIP-86 is not supported yet // Expected failures: st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/EIP158`, "bug in test") - st.fails(`^stRevertTest/RevertPrefoundEmptyOOG\.json/EIP158`, "bug in test") st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/Byzantium`, "bug in test") - st.fails(`^stRevertTest/RevertPrefoundEmptyOOG\.json/Byzantium`, "bug in test") st.fails(`^stRandom2/randomStatetest64[45]\.json/(EIP150|Frontier|Homestead)/.*`, "known bug #15119") - st.fails(`^stCreateTest/TransactionCollisionToEmpty\.json/EIP158/2`, "known bug ") - st.fails(`^stCreateTest/TransactionCollisionToEmpty\.json/EIP158/3`, "known bug ") - st.fails(`^stCreateTest/TransactionCollisionToEmpty\.json/Byzantium/2`, "known bug ") - st.fails(`^stCreateTest/TransactionCollisionToEmpty\.json/Byzantium/3`, "known bug ") st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) { for _, subtest := range test.Subtests() { From d985b9052ae08f2538e6caa7e6b5ef351a00bc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 27 Mar 2018 15:13:30 +0300 Subject: [PATCH 02/36] core/state: avoid linear overhead on journal dirty listing --- core/state/journal.go | 115 ++++++++++++++++++++++++------------- core/state/state_object.go | 6 +- core/state/statedb.go | 35 +++++------ core/state/statedb_test.go | 4 +- 4 files changed, 93 insertions(+), 67 deletions(-) diff --git a/core/state/journal.go b/core/state/journal.go index b00a462245..a03ca57dbc 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -22,43 +22,68 @@ import ( "github.com/ethereum/go-ethereum/common" ) +// journalEntry is a modification entry in the state change journal that can be +// reverted on demand. type journalEntry interface { - undo(*StateDB) - getAccount() *common.Address + // revert undoes the changes introduced by this journal entry. + revert(*StateDB) + + // dirtied returns the Ethereum address modified by this journal entry. + dirtied() *common.Address } +// journal contains the list of state modifications applied since the last state +// commit. These are tracked to be able to be reverted in case of an execution +// exception or revertal request. type journal struct { - entries []journalEntry - dirtyOverrides []common.Address + entries []journalEntry // Current changes tracked by the journal + dirties map[common.Address]int // Dirty accounts and the number of changes } +// newJournal create a new initialized journal. +func newJournal() *journal { + return &journal{ + dirties: make(map[common.Address]int), + } +} + +// append inserts a new modification entry to the end of the change journal. func (j *journal) append(entry journalEntry) { j.entries = append(j.entries, entry) + if addr := entry.dirtied(); addr != nil { + j.dirties[*addr]++ + } } -func (j *journal) flatten() map[common.Address]struct{} { +// revert undoes a batch of journalled modifications along with any reverted +// dirty handling too. +func (j *journal) revert(statedb *StateDB, snapshot int) { + for i := len(j.entries) - 1; i >= snapshot; i-- { + // Undo the changes made by the operation + j.entries[i].revert(statedb) - dirtyObjects := make(map[common.Address]struct{}) - for _, journalEntry := range j.entries { - if addr := journalEntry.getAccount(); addr != nil { - dirtyObjects[*addr] = struct{}{} + // Drop any dirty tracking induced by the change + if addr := j.entries[i].dirtied(); addr != nil { + if j.dirties[*addr]--; j.dirties[*addr] == 0 { + delete(j.dirties, *addr) + } } } - for _, addr := range j.dirtyOverrides { - dirtyObjects[addr] = struct{}{} - } - return dirtyObjects + j.entries = j.entries[:snapshot] } -// Length returns the number of journal entries in the journal -func (j *journal) Length() int { +// dirty explicitly sets an address to dirty, even if the change entries would +// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD +// precompile consensus exception. +func (j *journal) dirty(addr common.Address) { + j.dirties[addr]++ +} + +// length returns the current number of entries in the journal. +func (j *journal) length() int { return len(j.entries) } -func (j *journal) dirtyOverride(address common.Address) { - j.dirtyOverrides = append(j.dirtyOverrides, address) -} - type ( // Changes to the account trie. createObjectChange struct { @@ -108,78 +133,85 @@ type ( } ) -func (ch createObjectChange) undo(s *StateDB) { +func (ch createObjectChange) revert(s *StateDB) { delete(s.stateObjects, *ch.account) delete(s.stateObjectsDirty, *ch.account) } -func (ch createObjectChange) getAccount() *common.Address { +func (ch createObjectChange) dirtied() *common.Address { return ch.account } -func (ch resetObjectChange) undo(s *StateDB) { +func (ch resetObjectChange) revert(s *StateDB) { s.setStateObject(ch.prev) } -func (ch resetObjectChange) getAccount() *common.Address { +func (ch resetObjectChange) dirtied() *common.Address { return nil } -func (ch suicideChange) undo(s *StateDB) { +func (ch suicideChange) revert(s *StateDB) { obj := s.getStateObject(*ch.account) if obj != nil { obj.suicided = ch.prev obj.setBalance(ch.prevbalance) } } -func (ch suicideChange) getAccount() *common.Address { + +func (ch suicideChange) dirtied() *common.Address { return ch.account } var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") -func (ch touchChange) undo(s *StateDB) { +func (ch touchChange) revert(s *StateDB) { } -func (ch touchChange) getAccount() *common.Address { + +func (ch touchChange) dirtied() *common.Address { return ch.account } -func (ch balanceChange) undo(s *StateDB) { +func (ch balanceChange) revert(s *StateDB) { s.getStateObject(*ch.account).setBalance(ch.prev) } -func (ch balanceChange) getAccount() *common.Address { + +func (ch balanceChange) dirtied() *common.Address { return ch.account } -func (ch nonceChange) undo(s *StateDB) { +func (ch nonceChange) revert(s *StateDB) { s.getStateObject(*ch.account).setNonce(ch.prev) } -func (ch nonceChange) getAccount() *common.Address { +func (ch nonceChange) dirtied() *common.Address { return ch.account } -func (ch codeChange) undo(s *StateDB) { + +func (ch codeChange) revert(s *StateDB) { s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) } -func (ch codeChange) getAccount() *common.Address { + +func (ch codeChange) dirtied() *common.Address { return ch.account } -func (ch storageChange) undo(s *StateDB) { +func (ch storageChange) revert(s *StateDB) { s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) } -func (ch storageChange) getAccount() *common.Address { + +func (ch storageChange) dirtied() *common.Address { return ch.account } -func (ch refundChange) undo(s *StateDB) { +func (ch refundChange) revert(s *StateDB) { s.refund = ch.prev } -func (ch refundChange) getAccount() *common.Address { + +func (ch refundChange) dirtied() *common.Address { return nil } -func (ch addLogChange) undo(s *StateDB) { +func (ch addLogChange) revert(s *StateDB) { logs := s.logs[ch.txhash] if len(logs) == 1 { delete(s.logs, ch.txhash) @@ -188,14 +220,15 @@ func (ch addLogChange) undo(s *StateDB) { } s.logSize-- } -func (ch addLogChange) getAccount() *common.Address { + +func (ch addLogChange) dirtied() *common.Address { return nil } -func (ch addPreimageChange) undo(s *StateDB) { +func (ch addPreimageChange) revert(s *StateDB) { delete(s.preimages, ch.hash) } -func (ch addPreimageChange) getAccount() *common.Address { +func (ch addPreimageChange) dirtied() *common.Address { return nil } diff --git a/core/state/state_object.go b/core/state/state_object.go index 523bb7150c..3c40c20415 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -141,9 +141,9 @@ func (c *stateObject) touch() { account: &c.address, }) if c.address == ripemd { - //Explicitly put it in the dirty-cache, which is otherwise - // generated from flattened journals - c.db.journal.dirtyOverride(c.address) + // Explicitly put it in the dirty-cache, which is otherwise generated from + // flattened journals. + c.db.journal.dirty(c.address) } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 7a88b3b16c..c7ef49f64d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -76,7 +76,7 @@ type StateDB struct { // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. - journal journal + journal *journal validRevisions []revision nextRevisionId int @@ -96,6 +96,7 @@ func New(root common.Hash, db Database) (*StateDB, error) { stateObjectsDirty: make(map[common.Address]struct{}), logs: make(map[common.Hash][]*types.Log), preimages: make(map[common.Hash][]byte), + journal: newJournal(), }, nil } @@ -456,21 +457,20 @@ func (self *StateDB) Copy() *StateDB { self.lock.Lock() defer self.lock.Unlock() - dirtyObjects := self.journal.flatten() - // Copy all the basic fields, initialize the memory ones state := &StateDB{ db: self.db, trie: self.db.CopyTrie(self.trie), - stateObjects: make(map[common.Address]*stateObject, len(dirtyObjects)), - stateObjectsDirty: make(map[common.Address]struct{}, len(dirtyObjects)), + stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)), refund: self.refund, logs: make(map[common.Hash][]*types.Log, len(self.logs)), logSize: self.logSize, preimages: make(map[common.Hash][]byte), + journal: newJournal(), } // Copy the dirty states, logs, and preimages - for addr := range dirtyObjects { + for addr := range self.journal.dirties { state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) state.stateObjectsDirty[addr] = struct{}{} } @@ -488,7 +488,7 @@ func (self *StateDB) Copy() *StateDB { func (self *StateDB) Snapshot() int { id := self.nextRevisionId self.nextRevisionId++ - self.validRevisions = append(self.validRevisions, revision{id, self.journal.Length()}) + self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()}) return id } @@ -503,13 +503,8 @@ func (self *StateDB) RevertToSnapshot(revid int) { } snapshot := self.validRevisions[idx].journalIndex - // Replay the journal to undo changes. - for i := self.journal.Length() - 1; i >= snapshot; i-- { - self.journal.entries[i].undo(self) - } - self.journal.entries = self.journal.entries[:snapshot] - - // Remove invalidated snapshots from the stack. + // Replay the journal to undo changes and remove invalidated snapshots + self.journal.revert(self, snapshot) self.validRevisions = self.validRevisions[:idx] } @@ -521,8 +516,7 @@ func (self *StateDB) GetRefund() uint64 { // Finalise finalises the state by removing the self destructed objects // and clears the journal as well as the refunds. func (s *StateDB) Finalise(deleteEmptyObjects bool) { - - for addr, v := range s.journal.flatten() { + for addr := range s.journal.dirties { stateObject := s.stateObjects[addr] if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { s.deleteStateObject(stateObject) @@ -530,7 +524,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { stateObject.updateRoot(s.db) s.updateStateObject(stateObject) } - s.stateObjectsDirty[addr] = v + s.stateObjectsDirty[addr] = struct{}{} } // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() @@ -574,7 +568,7 @@ func (s *StateDB) DeleteSuicides() { } func (s *StateDB) clearJournalAndRefund() { - s.journal = journal{} + s.journal = newJournal() s.validRevisions = s.validRevisions[:0] s.refund = 0 } @@ -583,10 +577,9 @@ func (s *StateDB) clearJournalAndRefund() { func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() - for addr, v := range s.journal.flatten() { - s.stateObjectsDirty[addr] = v + for addr := range s.journal.dirties { + s.stateObjectsDirty[addr] = struct{}{} } - // Commit objects to the trie. for addr, stateObject := range s.stateObjects { _, isDirty := s.stateObjectsDirty[addr] diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 05bc0499b8..340c840f13 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -414,11 +414,11 @@ func (s *StateSuite) TestTouchDelete(c *check.C) { snapshot := s.state.Snapshot() s.state.AddBalance(common.Address{}, new(big.Int)) - if len(s.state.journal.flatten()) != 1 { + if len(s.state.journal.dirties) != 1 { c.Fatal("expected one dirty state object") } s.state.RevertToSnapshot(snapshot) - if len(s.state.journal.flatten()) != 0 { + if len(s.state.journal.dirties) != 0 { c.Fatal("expected no dirty state object") } } From 6cdfb9a3ebd03dc8864d1038e70430045da665f4 Mon Sep 17 00:00:00 2001 From: Li Xuanji Date: Tue, 3 Apr 2018 21:21:24 +0800 Subject: [PATCH 03/36] .gitattributes: enable solidity highlighting on github (#16425) --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index dfe0770424..0269fab9cb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ # Auto detect text files and perform LF normalization * text=auto +*.sol linguist-language=Solidity From d1af4e1a9eae903514dc6e0afd665218a90a19bf Mon Sep 17 00:00:00 2001 From: David Huie Date: Tue, 3 Apr 2018 08:12:00 -0700 Subject: [PATCH 04/36] crypto/secp256k1: catch curve parameter parse errors (#16392) --- crypto/secp256k1/curve.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crypto/secp256k1/curve.go b/crypto/secp256k1/curve.go index df80481857..f51be5e353 100644 --- a/crypto/secp256k1/curve.go +++ b/crypto/secp256k1/curve.go @@ -290,11 +290,11 @@ func init() { // See SEC 2 section 2.7.1 // curve parameters taken from: // http://www.secg.org/collateral/sec2_final.pdf - theCurve.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) - theCurve.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) - theCurve.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16) - theCurve.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) - theCurve.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) + theCurve.P = math.MustParseBig256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F") + theCurve.N = math.MustParseBig256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141") + theCurve.B = math.MustParseBig256("0x0000000000000000000000000000000000000000000000000000000000000007") + theCurve.Gx = math.MustParseBig256("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798") + theCurve.Gy = math.MustParseBig256("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8") theCurve.BitSize = 256 } From 5909482fb55095b58ff3f9d8207a4daa69defe26 Mon Sep 17 00:00:00 2001 From: Jia Chenhui Date: Tue, 3 Apr 2018 23:13:19 +0800 Subject: [PATCH 05/36] core/state: avoid redundant addition to code size cache (#16427) --- core/state/database.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/state/database.go b/core/state/database.go index 36926ec69d..c1b630991c 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -26,7 +26,7 @@ import ( lru "github.com/hashicorp/golang-lru" ) -// Trie cache generation limit after which to evic trie nodes from memory. +// Trie cache generation limit after which to evict trie nodes from memory. var MaxTrieCacheGen = uint16(120) const ( @@ -151,9 +151,6 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro return cached.(int), nil } code, err := db.ContractCode(addrHash, codeHash) - if err == nil { - db.codeSizeCache.Add(codeHash, len(code)) - } return len(code), err } From 2a4bd55b43df92fe2f83468656ca03199596bceb Mon Sep 17 00:00:00 2001 From: Nguyen Sy Thanh Son Date: Wed, 4 Apr 2018 17:22:26 +0700 Subject: [PATCH 06/36] cmd/geth: remove relOracle variable (#16434) --- cmd/geth/main.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 061384d1b3..d94154245f 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethclient" @@ -46,8 +45,6 @@ const ( var ( // Git SHA1 commit hash of the release (set via linker flags) gitCommit = "" - // Ethereum address of the Geth release oracle. - relOracle = common.HexToAddress("0xfa7b9770ca4cb04296cac84f37736d4041251cdf") // The app that holds all commands and flags. app = utils.NewApp(gitCommit, "the go-ethereum command line interface") // flags that configure the node From 7aad81f8815084c8ed032705fbaf6d3710e518cf Mon Sep 17 00:00:00 2001 From: Yusup Date: Wed, 4 Apr 2018 18:25:02 +0800 Subject: [PATCH 07/36] eth: fix typos (#16414) --- eth/backend.go | 4 ++-- eth/db_upgrade.go | 4 ++-- eth/downloader/downloader.go | 8 ++++---- eth/downloader/downloader_test.go | 2 +- eth/downloader/fakepeer.go | 6 +++--- eth/downloader/peer.go | 2 +- eth/downloader/queue.go | 2 +- eth/downloader/statesync.go | 10 +++++----- eth/fetcher/fetcher.go | 2 +- eth/filters/api.go | 2 +- eth/handler.go | 16 ++++++++-------- 11 files changed, 29 insertions(+), 29 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index 94aad23101..ffd5d85421 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -63,7 +63,7 @@ type Ethereum struct { chainConfig *params.ChainConfig // Channel for shutting down the service - shutdownChan chan bool // Channel for shutting down the ethereum + shutdownChan chan bool // Channel for shutting down the Ethereum stopDbUpgrade func() error // stop chain db sequential key upgrade // Handlers @@ -351,7 +351,7 @@ func (s *Ethereum) StartMining(local bool) error { if local { // If local (CPU) mining is started, we can disable the transaction rejection // mechanism introduced to speed sync times. CPU mining on mainnet is ludicrous - // so noone will ever hit this path, whereas marking sync done on CPU mining + // so none will ever hit this path, whereas marking sync done on CPU mining // will ensure that private networks work in single miner mode too. atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) } diff --git a/eth/db_upgrade.go b/eth/db_upgrade.go index d41afa17c0..96c584ac64 100644 --- a/eth/db_upgrade.go +++ b/eth/db_upgrade.go @@ -62,7 +62,7 @@ func upgradeDeduplicateData(db ethdb.Database) func() error { failed error ) for failed == nil && it.Next() { - // Skip any entries that don't look like old transaction meta entires (0x01) + // Skip any entries that don't look like old transaction meta entries (0x01) key := it.Key() if len(key) != common.HashLength+1 || key[common.HashLength] != 0x01 { continue @@ -86,7 +86,7 @@ func upgradeDeduplicateData(db ethdb.Database) func() error { } } // Convert the old metadata to a new lookup entry, delete duplicate data - if failed = db.Put(append([]byte("l"), hash...), it.Value()); failed == nil { // Write the new looku entry + if failed = db.Put(append([]byte("l"), hash...), it.Value()); failed == nil { // Write the new lookup entry if failed = db.Delete(hash); failed == nil { // Delete the duplicate transaction data if failed = db.Delete(append([]byte("receipts-"), hash...)); failed == nil { // Delete the duplicate receipt data if failed = db.Delete(key); failed != nil { // Delete the old transaction metadata diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 62842adbc6..9e49498998 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -47,7 +47,7 @@ var ( MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests - rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests + rttMaxEstimate = 20 * time.Second // Maximum round-trip time to target for download requests rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts @@ -884,7 +884,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64) // immediately to the header processor to keep the rest of the pipeline full even // in the case of header stalls. // -// The method returs the entire filled skeleton and also the number of headers +// The method returns the entire filled skeleton and also the number of headers // already forwarded for processing. func (d *Downloader) fillHeaderSkeleton(from uint64, skeleton []*types.Header) ([]*types.Header, int, error) { log.Debug("Filling up skeleton", "from", from) @@ -1377,7 +1377,7 @@ func (d *Downloader) processFastSyncContent(latest *types.Header) error { pivot = height - uint64(fsMinFullBlocks) } // To cater for moving pivot points, track the pivot block and subsequently - // accumulated download results separatey. + // accumulated download results separately. var ( oldPivot *fetchResult // Locked in pivot block, might change eventually oldTail []*fetchResult // Downloaded content after the pivot @@ -1615,7 +1615,7 @@ func (d *Downloader) qosReduceConfidence() { // // Note, the returned RTT is .9 of the actually estimated RTT. The reason is that // the downloader tries to adapt queries to the RTT, so multiple RTT values can -// be adapted to, but smaller ones are preffered (stabler download stream). +// be adapted to, but smaller ones are preferred (stabler download stream). func (d *Downloader) requestRTT() time.Duration { return time.Duration(atomic.LoadUint64(&d.rttEstimate)) * 9 / 10 } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index cb671a7df4..e85e234c0e 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -159,7 +159,7 @@ func (dl *downloadTester) makeChainFork(n, f int, parent *types.Block, parentRec // Create the common suffix hashes, headers, blocks, receipts := dl.makeChain(n-f, 0, parent, parentReceipts, false) - // Create the forks, making the second heavyer if non balanced forks were requested + // Create the forks, making the second heavier if non balanced forks were requested hashes1, headers1, blocks1, receipts1 := dl.makeChain(f, 1, blocks[hashes[0]], receipts[hashes[0]], false) hashes1 = append(hashes1, hashes[1:]...) diff --git a/eth/downloader/fakepeer.go b/eth/downloader/fakepeer.go index b45acff7d0..5248e7fb0c 100644 --- a/eth/downloader/fakepeer.go +++ b/eth/downloader/fakepeer.go @@ -27,7 +27,7 @@ import ( // FakePeer is a mock downloader peer that operates on a local database instance // instead of being an actual live node. It's useful for testing and to implement -// sync commands from an xisting local database. +// sync commands from an existing local database. type FakePeer struct { id string db ethdb.Database @@ -48,7 +48,7 @@ func (p *FakePeer) Head() (common.Hash, *big.Int) { } // RequestHeadersByHash implements downloader.Peer, returning a batch of headers -// defined by the origin hash and the associaed query parameters. +// defined by the origin hash and the associated query parameters. func (p *FakePeer) RequestHeadersByHash(hash common.Hash, amount int, skip int, reverse bool) error { var ( headers []*types.Header @@ -92,7 +92,7 @@ func (p *FakePeer) RequestHeadersByHash(hash common.Hash, amount int, skip int, } // RequestHeadersByNumber implements downloader.Peer, returning a batch of headers -// defined by the origin number and the associaed query parameters. +// defined by the origin number and the associated query parameters. func (p *FakePeer) RequestHeadersByNumber(number uint64, amount int, skip int, reverse bool) error { var ( headers []*types.Header diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go index a4aa861146..428a60f8a1 100644 --- a/eth/downloader/peer.go +++ b/eth/downloader/peer.go @@ -551,7 +551,7 @@ func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peerC // medianRTT returns the median RTT of the peerset, considering only the tuning // peers if there are more peers available. func (ps *peerSet) medianRTT() time.Duration { - // Gather all the currnetly measured round trip times + // Gather all the currently measured round trip times ps.lock.RLock() defer ps.lock.RUnlock() diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 359cce54b5..bbe0aed5dc 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -275,7 +275,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) { if q.headerResults != nil { panic("skeleton assembly already in progress") } - // Shedule all the header retrieval tasks for the skeleton assembly + // Schedule all the header retrieval tasks for the skeleton assembly q.headerTaskPool = make(map[uint64]*types.Header) q.headerTaskQueue = prque.New() q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index ee6c7b4914..4071d0ad98 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -31,7 +31,7 @@ import ( "github.com/ethereum/go-ethereum/trie" ) -// stateReq represents a batch of state fetch requests groupped together into +// stateReq represents a batch of state fetch requests grouped together into // a single data retrieval network packet. type stateReq struct { items []common.Hash // Hashes of the state items to download @@ -139,7 +139,7 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync { // Handle incoming state packs: case pack := <-d.stateCh: - // Discard any data not requested (or previsouly timed out) + // Discard any data not requested (or previously timed out) req := active[pack.PeerId()] if req == nil { log.Debug("Unrequested node data", "peer", pack.PeerId(), "len", pack.Items()) @@ -182,7 +182,7 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync { case req := <-d.trackStateReq: // If an active request already exists for this peer, we have a problem. In // theory the trie node schedule must never assign two requests to the same - // peer. In practive however, a peer might receive a request, disconnect and + // peer. In practice however, a peer might receive a request, disconnect and // immediately reconnect before the previous times out. In this case the first // request is never honored, alas we must not silently overwrite it, as that // causes valid requests to go missing and sync to get stuck. @@ -228,7 +228,7 @@ type stateSync struct { err error // Any error hit during sync (set before completion) } -// stateTask represents a single trie node download taks, containing a set of +// stateTask represents a single trie node download task, containing a set of // peers already attempted retrieval from to detect stalled syncs and abort. type stateTask struct { attempts map[string]struct{} @@ -333,7 +333,7 @@ func (s *stateSync) commit(force bool) error { return nil } -// assignTasks attempts to assing new tasks to all idle peers, either from the +// assignTasks attempts to assign new tasks to all idle peers, either from the // batch currently being retried, or fetching new data from the trie sync itself. func (s *stateSync) assignTasks() { // Iterate over all idle peers and try to assign them state fetches diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index db554e1440..0c679cec3a 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -127,7 +127,7 @@ type Fetcher struct { // Block cache queue *prque.Prque // Queue containing the import operations (block number sorted) queues map[string]int // Per peer block counts to prevent memory exhaustion - queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports) + queued map[common.Hash]*inject // Set of already queued blocks (to dedupe imports) // Callbacks getBlock blockRetrievalFn // Retrieves a block from the local chain diff --git a/eth/filters/api.go b/eth/filters/api.go index 406c9442e0..ec403709c5 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -98,7 +98,7 @@ func (api *PublicFilterAPI) timeoutLoop() { // NewPendingTransactionFilter creates a filter that fetches pending transaction hashes // as transactions enter the pending state. // -// It is part of the filter package because this filter can be used throug the +// It is part of the filter package because this filter can be used through the // `eth_getFilterChanges` polling method that is also used for log filters. // // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter diff --git a/eth/handler.go b/eth/handler.go index 3fae0cd00d..4069359c9e 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -96,8 +96,8 @@ type ProtocolManager struct { wg sync.WaitGroup } -// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable -// with the ethereum network. +// NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable +// with the Ethereum network. func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ @@ -498,20 +498,20 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Deliver them all to the downloader for queuing - trasactions := make([][]*types.Transaction, len(request)) + transactions := make([][]*types.Transaction, len(request)) uncles := make([][]*types.Header, len(request)) for i, body := range request { - trasactions[i] = body.Transactions + transactions[i] = body.Transactions uncles[i] = body.Uncles } // Filter out any explicitly requested bodies, deliver the rest to the downloader - filter := len(trasactions) > 0 || len(uncles) > 0 + filter := len(transactions) > 0 || len(uncles) > 0 if filter { - trasactions, uncles = pm.fetcher.FilterBodies(p.id, trasactions, uncles, time.Now()) + transactions, uncles = pm.fetcher.FilterBodies(p.id, transactions, uncles, time.Now()) } - if len(trasactions) > 0 || len(uncles) > 0 || !filter { - err := pm.downloader.DeliverBodies(p.id, trasactions, uncles) + if len(transactions) > 0 || len(uncles) > 0 || !filter { + err := pm.downloader.DeliverBodies(p.id, transactions, uncles) if err != nil { log.Debug("Failed to deliver bodies", "err", err) } From 6ab9f0a19fbf548a78d08a526f522f41d8175a4e Mon Sep 17 00:00:00 2001 From: Ricardo Domingos Date: Wed, 4 Apr 2018 13:42:36 +0200 Subject: [PATCH 08/36] accounts/abi: improve test coverage (#16044) --- accounts/abi/numbers.go | 36 +++++++++---------- accounts/abi/reflect.go | 20 +++++------ accounts/abi/type.go | 2 +- accounts/abi/type_test.go | 69 +++++++++++++++++++------------------ accounts/abi/unpack_test.go | 17 +++++++++ 5 files changed, 82 insertions(+), 62 deletions(-) diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go index 9ad99f90d2..0cd97cc66f 100644 --- a/accounts/abi/numbers.go +++ b/accounts/abi/numbers.go @@ -25,23 +25,23 @@ import ( ) var ( - big_t = reflect.TypeOf(&big.Int{}) - derefbig_t = reflect.TypeOf(big.Int{}) - uint8_t = reflect.TypeOf(uint8(0)) - uint16_t = reflect.TypeOf(uint16(0)) - uint32_t = reflect.TypeOf(uint32(0)) - uint64_t = reflect.TypeOf(uint64(0)) - int_t = reflect.TypeOf(int(0)) - int8_t = reflect.TypeOf(int8(0)) - int16_t = reflect.TypeOf(int16(0)) - int32_t = reflect.TypeOf(int32(0)) - int64_t = reflect.TypeOf(int64(0)) - address_t = reflect.TypeOf(common.Address{}) - int_ts = reflect.TypeOf([]int(nil)) - int8_ts = reflect.TypeOf([]int8(nil)) - int16_ts = reflect.TypeOf([]int16(nil)) - int32_ts = reflect.TypeOf([]int32(nil)) - int64_ts = reflect.TypeOf([]int64(nil)) + bigT = reflect.TypeOf(&big.Int{}) + derefbigT = reflect.TypeOf(big.Int{}) + uint8T = reflect.TypeOf(uint8(0)) + uint16T = reflect.TypeOf(uint16(0)) + uint32T = reflect.TypeOf(uint32(0)) + uint64T = reflect.TypeOf(uint64(0)) + intT = reflect.TypeOf(int(0)) + int8T = reflect.TypeOf(int8(0)) + int16T = reflect.TypeOf(int16(0)) + int32T = reflect.TypeOf(int32(0)) + int64T = reflect.TypeOf(int64(0)) + addressT = reflect.TypeOf(common.Address{}) + intTS = reflect.TypeOf([]int(nil)) + int8TS = reflect.TypeOf([]int8(nil)) + int16TS = reflect.TypeOf([]int16(nil)) + int32TS = reflect.TypeOf([]int32(nil)) + int64TS = reflect.TypeOf([]int64(nil)) ) // U256 converts a big Int into a 256bit EVM number. @@ -52,7 +52,7 @@ func U256(n *big.Int) []byte { // checks whether the given reflect value is signed. This also works for slices with a number type func isSigned(v reflect.Value) bool { switch v.Type() { - case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t: + case intTS, int8TS, int16TS, int32TS, int64TS, intT, int8T, int16T, int32T, int64T: return true } return false diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 2e6bf7098f..5620a70845 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -24,7 +24,7 @@ import ( // indirect recursively dereferences the value until it either gets the value // or finds a big.Int func indirect(v reflect.Value) reflect.Value { - if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbig_t { + if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbigT { return indirect(v.Elem()) } return v @@ -36,26 +36,26 @@ func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type) switch size { case 8: if unsigned { - return reflect.Uint8, uint8_t + return reflect.Uint8, uint8T } - return reflect.Int8, int8_t + return reflect.Int8, int8T case 16: if unsigned { - return reflect.Uint16, uint16_t + return reflect.Uint16, uint16T } - return reflect.Int16, int16_t + return reflect.Int16, int16T case 32: if unsigned { - return reflect.Uint32, uint32_t + return reflect.Uint32, uint32T } - return reflect.Int32, int32_t + return reflect.Int32, int32T case 64: if unsigned { - return reflect.Uint64, uint64_t + return reflect.Uint64, uint64T } - return reflect.Int64, int64_t + return reflect.Int64, int64T } - return reflect.Ptr, big_t + return reflect.Ptr, bigT } // mustArrayToBytesSlice creates a new byte slice with the exact same size as value diff --git a/accounts/abi/type.go b/accounts/abi/type.go index a1f13ffa29..9de36daffb 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -135,7 +135,7 @@ func NewType(t string) (typ Type, err error) { typ.Type = reflect.TypeOf(bool(false)) case "address": typ.Kind = reflect.Array - typ.Type = address_t + typ.Type = addressT typ.Size = 20 typ.T = AddressTy case "string": diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go index e55af12939..f6b36f18fd 100644 --- a/accounts/abi/type_test.go +++ b/accounts/abi/type_test.go @@ -46,36 +46,36 @@ func TestTypeRegexp(t *testing.T) { {"bool[2][2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}}, {"bool[][][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}}, {"bool[][2][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}}, - {"int8", Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}}, - {"int16", Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}}, - {"int32", Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}}, - {"int64", Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}}, - {"int256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}}, - {"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}}, - {"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}}, - {"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}}, - {"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}}, - {"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}}, - {"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}}, - {"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}}, - {"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}}, - {"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}}, - {"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}}, - {"uint8", Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}}, - {"uint16", Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}}, - {"uint32", Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}}, - {"uint64", Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}}, - {"uint256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}}, - {"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}}, - {"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}}, - {"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}}, - {"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}}, - {"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}}, - {"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}}, - {"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}}, - {"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}}, - {"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}}, - {"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}}, + {"int8", Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}}, + {"int16", Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}}, + {"int32", Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}}, + {"int64", Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}}, + {"int256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}}, + {"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}}, + {"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}}, + {"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}}, + {"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}}, + {"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}}, + {"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}}, + {"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}}, + {"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}}, + {"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}}, + {"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}}, + {"uint8", Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}}, + {"uint16", Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}}, + {"uint32", Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}}, + {"uint64", Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}}, + {"uint256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}}, + {"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}}, + {"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}}, + {"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}}, + {"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}}, + {"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}}, + {"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}}, + {"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}}, + {"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}}, + {"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}}, + {"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}}, {"bytes32", Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}}, {"bytes[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}}, {"bytes[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}}, @@ -84,9 +84,9 @@ func TestTypeRegexp(t *testing.T) { {"string", Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}}, {"string[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}}, {"string[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}}, - {"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}}, - {"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}}, - {"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}}, + {"address", Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}}, + {"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}}, + {"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}}, // TODO when fixed types are implemented properly // {"fixed", Type{}}, // {"fixed128x128", Type{}}, @@ -252,6 +252,9 @@ func TestTypeCheck(t *testing.T) { {"bytes20", common.Address{}, ""}, {"address", [20]byte{}, ""}, {"address", common.Address{}, ""}, + {"bytes32[]]", "", "invalid arg type in abi"}, + {"invalidType", "", "unsupported arg type: invalidType"}, + {"invalidSlice[]", "", "unsupported arg type: invalidSlice"}, } { typ, err := NewType(test.typ) if err != nil && len(test.err) == 0 { diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index ee62567094..bdbab10b4f 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -56,6 +56,23 @@ var unpackTests = []unpackTest{ enc: "0000000000000000000000000000000000000000000000000000000000000001", want: true, }, + { + def: `[{ "type": "bool" }]`, + enc: "0000000000000000000000000000000000000000000000000000000000000000", + want: false, + }, + { + def: `[{ "type": "bool" }]`, + enc: "0000000000000000000000000000000000000000000000000001000000000001", + want: false, + err: "abi: improperly encoded boolean value", + }, + { + def: `[{ "type": "bool" }]`, + enc: "0000000000000000000000000000000000000000000000000000000000000003", + want: false, + err: "abi: improperly encoded boolean value", + }, { def: `[{"type": "uint32"}]`, enc: "0000000000000000000000000000000000000000000000000000000000000001", From 1e248f3a6e14f3bfc9ebe1b315c4194300220f68 Mon Sep 17 00:00:00 2001 From: Giovanni HoSang Date: Wed, 4 Apr 2018 06:25:34 -0700 Subject: [PATCH 09/36] README: change 'built in' to 'built-in' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d0d4d35d4..b2e992a4ef 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Do not forget `--rpcaddr 0.0.0.0`, if you want to access RPC from other containe ### Programatically interfacing Geth nodes As a developer, sooner rather than later you'll want to start interacting with Geth and the Ethereum -network via your own programs and not manually through the console. To aid this, Geth has built in +network via your own programs and not manually through the console. To aid this, Geth has built-in support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) and [Geth specific APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)). These can be exposed via HTTP, WebSockets and IPC (unix sockets on unix based platforms, and named pipes on Windows). From ec8ee611caefb5c5ad5d796178e94c1919260df4 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Thu, 5 Apr 2018 14:13:02 +0200 Subject: [PATCH 10/36] core/types: remove String methods from struct types (#16205) Most of these methods did not contain all the relevant information inside the object and were not using a similar formatting type. Moreover, the existence of a suboptimal String method breaks usage with more advanced data dumping tools like go-spew. --- core/database_util_test.go | 2 +- core/types/block.go | 35 ------------------------- core/types/log.go | 5 ---- core/types/receipt.go | 8 ------ core/types/transaction.go | 53 -------------------------------------- internal/ethapi/api.go | 3 ++- mobile/types.go | 24 ----------------- 7 files changed, 3 insertions(+), 127 deletions(-) diff --git a/core/database_util_test.go b/core/database_util_test.go index ab4e45a478..aa87fa6f86 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -317,7 +317,7 @@ func TestLookupStorage(t *testing.T) { if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i) } - if tx.String() != txn.String() { + if tx.Hash() != txn.Hash() { t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx) } } diff --git a/core/types/block.go b/core/types/block.go index 92b868d9da..ae1b4299d3 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -19,7 +19,6 @@ package types import ( "encoding/binary" - "fmt" "io" "math/big" "sort" @@ -389,40 +388,6 @@ func (b *Block) Hash() common.Hash { return v } -func (b *Block) String() string { - str := fmt.Sprintf(`Block(#%v): Size: %v { -MinerHash: %x -%v -Transactions: -%v -Uncles: -%v -} -`, b.Number(), b.Size(), b.header.HashNoNonce(), b.header, b.transactions, b.uncles) - return str -} - -func (h *Header) String() string { - return fmt.Sprintf(`Header(%x): -[ - ParentHash: %x - UncleHash: %x - Coinbase: %x - Root: %x - TxSha %x - ReceiptSha: %x - Bloom: %x - Difficulty: %v - Number: %v - GasLimit: %v - GasUsed: %v - Time: %v - Extra: %s - MixDigest: %x - Nonce: %x -]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce) -} - type Blocks []*Block type BlockBy func(b1, b2 *Block) bool diff --git a/core/types/log.go b/core/types/log.go index be5de38da7..b629b47ed5 100644 --- a/core/types/log.go +++ b/core/types/log.go @@ -17,7 +17,6 @@ package types import ( - "fmt" "io" "github.com/ethereum/go-ethereum/common" @@ -95,10 +94,6 @@ func (l *Log) DecodeRLP(s *rlp.Stream) error { return err } -func (l *Log) String() string { - return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index) -} - // LogForStorage is a wrapper around a Log that flattens and parses the entire content of // a log including non-consensus fields. type LogForStorage Log diff --git a/core/types/receipt.go b/core/types/receipt.go index f945f6f6a2..613f03d50f 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -149,14 +149,6 @@ func (r *Receipt) Size() common.StorageSize { return size } -// String implements the Stringer interface. -func (r *Receipt) String() string { - if len(r.PostState) == 0 { - return fmt.Sprintf("receipt{status=%d cgas=%v bloom=%x logs=%v}", r.Status, r.CumulativeGasUsed, r.Bloom, r.Logs) - } - return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs) -} - // ReceiptForStorage is a wrapper around a Receipt that flattens and parses the // entire content of a receipt, as opposed to only the consensus fields originally. type ReceiptForStorage Receipt diff --git a/core/types/transaction.go b/core/types/transaction.go index 5660582baf..70d757c94e 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -19,7 +19,6 @@ package types import ( "container/heap" "errors" - "fmt" "io" "math/big" "sync/atomic" @@ -262,58 +261,6 @@ func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { return tx.data.V, tx.data.R, tx.data.S } -func (tx *Transaction) String() string { - var from, to string - if tx.data.V != nil { - // make a best guess about the signer and use that to derive - // the sender. - signer := deriveSigner(tx.data.V) - if f, err := Sender(signer, tx); err != nil { // derive but don't cache - from = "[invalid sender: invalid sig]" - } else { - from = fmt.Sprintf("%x", f[:]) - } - } else { - from = "[invalid sender: nil V field]" - } - - if tx.data.Recipient == nil { - to = "[contract creation]" - } else { - to = fmt.Sprintf("%x", tx.data.Recipient[:]) - } - enc, _ := rlp.EncodeToBytes(&tx.data) - return fmt.Sprintf(` - TX(%x) - Contract: %v - From: %s - To: %s - Nonce: %v - GasPrice: %#x - GasLimit %#x - Value: %#x - Data: 0x%x - V: %#x - R: %#x - S: %#x - Hex: %x -`, - tx.Hash(), - tx.data.Recipient == nil, - from, - to, - tx.data.AccountNonce, - tx.data.Price, - tx.data.GasLimit, - tx.data.Amount, - tx.data.Payload, - tx.data.V, - tx.data.R, - tx.data.S, - enc, - ) -} - // Transactions is a Transaction slice type for basic sorting. type Transactions []*Transaction diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6525aa212c..e2bfbaf307 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -25,6 +25,7 @@ import ( "strings" "time" + "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" @@ -1388,7 +1389,7 @@ func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (strin if block == nil { return "", fmt.Errorf("block #%d not found", number) } - return block.String(), nil + return spew.Sdump(block), nil } // SeedHash retrieves the seed hash of a block. diff --git a/mobile/types.go b/mobile/types.go index 4790afceff..24cd7ebf11 100644 --- a/mobile/types.go +++ b/mobile/types.go @@ -97,12 +97,6 @@ func (h *Header) EncodeJSON() (string, error) { return string(data), err } -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the header for debugging purposes. -func (h *Header) String() string { - return h.header.String() -} - func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} } func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} } func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} } @@ -174,12 +168,6 @@ func (b *Block) EncodeJSON() (string, error) { return string(data), err } -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the block for debugging purposes. -func (b *Block) String() string { - return b.block.String() -} - func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} } func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} } func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} } @@ -249,12 +237,6 @@ func (tx *Transaction) EncodeJSON() (string, error) { return string(data), err } -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the transaction for debugging purposes. -func (tx *Transaction) String() string { - return tx.tx.String() -} - func (tx *Transaction) GetData() []byte { return tx.tx.Data() } func (tx *Transaction) GetGas() int64 { return int64(tx.tx.Gas()) } func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} } @@ -347,12 +329,6 @@ func (r *Receipt) EncodeJSON() (string, error) { return string(data), err } -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the transaction receipt for debugging purposes. -func (r *Receipt) String() string { - return r.receipt.String() -} - func (r *Receipt) GetPostState() []byte { return r.receipt.PostState } func (r *Receipt) GetCumulativeGasUsed() int64 { return int64(r.receipt.CumulativeGasUsed) } func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} } From 50dbe8e2444cfc171930cb82cc99017f6a0aadf2 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 5 Apr 2018 14:14:32 +0200 Subject: [PATCH 11/36] Dockerfile: use non-privileged user account (#16052) --- Dockerfile | 6 ++++++ Dockerfile.alltools | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Dockerfile b/Dockerfile index 29cdc80f96..a5f450d19b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,5 +12,11 @@ FROM alpine:latest RUN apk add --no-cache ca-certificates COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/ +RUN addgroup -g 1000 geth && \ + adduser -h /root -D -u 1000 -G geth geth && \ + chown geth:geth /root + +USER geth + EXPOSE 8545 8546 30303 30303/udp 30304/udp ENTRYPOINT ["geth"] diff --git a/Dockerfile.alltools b/Dockerfile.alltools index 1047738d25..2175edbcb7 100644 --- a/Dockerfile.alltools +++ b/Dockerfile.alltools @@ -12,4 +12,10 @@ FROM alpine:latest RUN apk add --no-cache ca-certificates COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/ +RUN addgroup -g 1000 geth && \ + adduser -h /root -D -u 1000 -G geth geth \ + chown geth:geth /root + +USER geth + EXPOSE 8545 8546 30303 30303/udp 30304/udp From c43792a42cad46a15ee00417d52bd3859a98500c Mon Sep 17 00:00:00 2001 From: Zhenguo Niu Date: Fri, 6 Apr 2018 18:42:11 +0800 Subject: [PATCH 12/36] cmd/geth: update template for 'geth bug' command (#16350) --- cmd/geth/bugcmd.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/cmd/geth/bugcmd.go b/cmd/geth/bugcmd.go index ce9dbe6c0a..51187ac90e 100644 --- a/cmd/geth/bugcmd.go +++ b/cmd/geth/bugcmd.go @@ -49,15 +49,17 @@ func reportBug(ctx *cli.Context) error { // execute template and write contents to buff var buff bytes.Buffer - fmt.Fprintln(&buff, header) + fmt.Fprintln(&buff, "#### System information") + fmt.Fprintln(&buff) fmt.Fprintln(&buff, "Version:", params.Version) fmt.Fprintln(&buff, "Go Version:", runtime.Version()) fmt.Fprintln(&buff, "OS:", runtime.GOOS) printOSDetails(&buff) + fmt.Fprintln(&buff, header) // open a new GH issue if !browser.Open(issueUrl + "?body=" + url.QueryEscape(buff.String())) { - fmt.Printf("Please file a new issue at %s using this template:\n%s", issueUrl, buff.String()) + fmt.Printf("Please file a new issue at %s using this template:\n\n%s", issueUrl, buff.String()) } return nil } @@ -97,13 +99,15 @@ func printCmdOut(w io.Writer, prefix, path string, args ...string) { fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out)) } -const header = `Please answer these questions before submitting your issue. Thanks! +const header = ` +#### Expected behaviour -#### What did you do? - -#### What did you expect to see? - -#### What did you see instead? - -#### System details + +#### Actual behaviour + + +#### Steps to reproduce the behaviour + + +#### Backtrace ` From 3ebcf92b423e67b58a72a3fc126449e4e97bc4c8 Mon Sep 17 00:00:00 2001 From: dm4 Date: Fri, 6 Apr 2018 18:43:36 +0800 Subject: [PATCH 13/36] cmd/evm: print vm output when debug flag is on (#16326) --- cmd/evm/runner.go | 5 ++--- core/vm/logger.go | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 8a7399840c..c13e9fb335 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -76,6 +76,7 @@ func runCmd(ctx *cli.Context) error { logconfig := &vm.LogConfig{ DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + Debug: ctx.GlobalBool(DebugFlag.Name), } var ( @@ -234,9 +235,7 @@ Gas used: %d `, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas) } - if tracer != nil { - tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err) - } else { + if tracer == nil { fmt.Printf("0x%x\n", ret) if err != nil { fmt.Printf(" error: %v\n", err) diff --git a/core/vm/logger.go b/core/vm/logger.go index 4c820d8b53..dde1903bf2 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -45,6 +45,7 @@ type LogConfig struct { DisableMemory bool // disable memory capture DisableStack bool // disable stack capture DisableStorage bool // disable storage capture + Debug bool // print output during capture end Limit int // maximum length of output, but zero means unlimited } @@ -184,6 +185,12 @@ func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost ui func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { l.output = output l.err = err + if l.cfg.Debug { + fmt.Printf("0x%x\n", output) + if err != nil { + fmt.Printf(" error: %v\n", err) + } + } return nil } From 8de655ef3a60053b564f609fd5c889face69f431 Mon Sep 17 00:00:00 2001 From: DoubleWoodH <3155965489@qq.com> Date: Mon, 9 Apr 2018 18:38:01 +0800 Subject: [PATCH 14/36] bmt: fix comment typos (#16461) --- bmt/bmt.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bmt/bmt.go b/bmt/bmt.go index 4b65b1d94a..3408758675 100644 --- a/bmt/bmt.go +++ b/bmt/bmt.go @@ -75,7 +75,7 @@ type Hasher struct { blocksize int // segment size (size of hash) also for hash.Hash count int // segment count size int // for hash.Hash same as hashsize - cur int // cursor position for righmost currently open chunk + cur int // cursor position for rightmost currently open chunk segment []byte // the rightmost open segment (not complete) depth int // index of last level result chan []byte // result channel @@ -149,7 +149,7 @@ func NewTreePool(hasher BaseHasher, segmentCount, capacity int) *TreePool { } } -// Drain drains the pool uptil it has no more than n resources +// Drain drains the pool until it has no more than n resources func (self *TreePool) Drain(n int) { self.lock.Lock() defer self.lock.Unlock() @@ -412,11 +412,10 @@ func (self *Hasher) Reset() { // ResetWithLength needs to be called before writing to the hasher // the argument is supposed to be the byte slice binary representation of -// the legth of the data subsumed under the hash +// the length of the data subsumed under the hash func (self *Hasher) ResetWithLength(l []byte) { self.Reset() self.blockLength = l - } // Release gives back the Tree to the pool whereby it unlocks @@ -531,7 +530,7 @@ func (self *Hasher) finalise(n *Node, i int) (d int) { for { // when the final segment's path is going via left segments // the incoming data is pushed to the parent upon pulling the left - // we do not need toogle the state since this condition is + // we do not need toggle the state since this condition is // detectable n.unbalanced = isLeft n.right = nil From 315b9b18df7994d56f3570c509fc60ad6e3d570f Mon Sep 17 00:00:00 2001 From: Ivo Georgiev Date: Mon, 9 Apr 2018 13:40:58 +0300 Subject: [PATCH 15/36] ethclient: remove empty object in newHeads subscription call (#16454) --- ethclient/ethclient.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 87a912901a..7349d6fbad 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -296,7 +296,7 @@ func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, err // SubscribeNewHead subscribes to notifications about the current blockchain head // on the given channel. func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { - return ec.c.EthSubscribe(ctx, ch, "newHeads", map[string]struct{}{}) + return ec.c.EthSubscribe(ctx, ch, "newHeads") } // State Access From 0fac705ed0a2efcda4da9dce8971bbda73f299d5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Apr 2018 13:47:06 +0200 Subject: [PATCH 16/36] compression/rle: delete RLE compression (#16468) --- compression/rle/read_write.go | 101 ----------------------------- compression/rle/read_write_test.go | 50 -------------- ethdb/database.go | 6 -- swarm/storage/database.go | 15 +---- 4 files changed, 2 insertions(+), 170 deletions(-) delete mode 100644 compression/rle/read_write.go delete mode 100644 compression/rle/read_write_test.go diff --git a/compression/rle/read_write.go b/compression/rle/read_write.go deleted file mode 100644 index 0e7ad90aec..0000000000 --- a/compression/rle/read_write.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2014 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 . - -// Package rle implements the run-length encoding used for Ethereum data. -package rle - -import ( - "bytes" - "errors" - - "github.com/ethereum/go-ethereum/crypto" -) - -const ( - token byte = 0xfe - emptyShaToken = 0xfd - emptyListShaToken = 0xfe - tokenToken = 0xff -) - -var empty = crypto.Keccak256([]byte("")) -var emptyList = crypto.Keccak256([]byte{0x80}) - -func Decompress(dat []byte) ([]byte, error) { - buf := new(bytes.Buffer) - - for i := 0; i < len(dat); i++ { - if dat[i] == token { - if i+1 < len(dat) { - switch dat[i+1] { - case emptyShaToken: - buf.Write(empty) - case emptyListShaToken: - buf.Write(emptyList) - case tokenToken: - buf.WriteByte(token) - default: - buf.Write(make([]byte, int(dat[i+1]-2))) - } - i++ - } else { - return nil, errors.New("error reading bytes. token encountered without proceeding bytes") - } - } else { - buf.WriteByte(dat[i]) - } - } - - return buf.Bytes(), nil -} - -func compressChunk(dat []byte) (ret []byte, n int) { - switch { - case dat[0] == token: - return []byte{token, tokenToken}, 1 - case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0: - j := 0 - for j <= 254 && j < len(dat) { - if dat[j] != 0 { - break - } - j++ - } - return []byte{token, byte(j + 2)}, j - case len(dat) >= 32: - if dat[0] == empty[0] && bytes.Equal(dat[:32], empty) { - return []byte{token, emptyShaToken}, 32 - } else if dat[0] == emptyList[0] && bytes.Equal(dat[:32], emptyList) { - return []byte{token, emptyListShaToken}, 32 - } - fallthrough - default: - return dat[:1], 1 - } -} - -func Compress(dat []byte) []byte { - buf := new(bytes.Buffer) - - i := 0 - for i < len(dat) { - b, n := compressChunk(dat[i:]) - buf.Write(b) - i += n - } - - return buf.Bytes() -} diff --git a/compression/rle/read_write_test.go b/compression/rle/read_write_test.go deleted file mode 100644 index b36f7907bc..0000000000 --- a/compression/rle/read_write_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2014 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 . - -package rle - -import ( - "testing" - - checker "gopkg.in/check.v1" -) - -func Test(t *testing.T) { checker.TestingT(t) } - -type CompressionRleSuite struct{} - -var _ = checker.Suite(&CompressionRleSuite{}) - -func (s *CompressionRleSuite) TestDecompressSimple(c *checker.C) { - exp := []byte{0xc5, 0xd2, 0x46, 0x1, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x3, 0xc0, 0xe5, 0x0, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x4, 0x5d, 0x85, 0xa4, 0x70} - res, err := Decompress([]byte{token, 0xfd}) - c.Assert(err, checker.IsNil) - c.Assert(res, checker.DeepEquals, exp) - - exp = []byte{0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x1, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21} - res, err = Decompress([]byte{token, 0xfe}) - c.Assert(err, checker.IsNil) - c.Assert(res, checker.DeepEquals, exp) - - res, err = Decompress([]byte{token, 0xff}) - c.Assert(err, checker.IsNil) - c.Assert(res, checker.DeepEquals, []byte{token}) - - res, err = Decompress([]byte{token, 12}) - c.Assert(err, checker.IsNil) - c.Assert(res, checker.DeepEquals, make([]byte, 10)) - -} diff --git a/ethdb/database.go b/ethdb/database.go index 30ed37dc7b..243b7f8d3c 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -91,9 +91,6 @@ func (db *LDBDatabase) Path() string { // Put puts the given key / value to the queue func (db *LDBDatabase) Put(key []byte, value []byte) error { - // Generate the data to write to disk, update the meter and write - //value = rle.Compress(value) - return db.db.Put(key, value, nil) } @@ -103,18 +100,15 @@ func (db *LDBDatabase) Has(key []byte) (bool, error) { // Get returns the given key if it's present. func (db *LDBDatabase) Get(key []byte) ([]byte, error) { - // Retrieve the key and increment the miss counter if not found dat, err := db.db.Get(key, nil) if err != nil { return nil, err } return dat, nil - //return rle.Decompress(dat) } // Delete deletes the key from the queue and database func (db *LDBDatabase) Delete(key []byte) error { - // Execute the actual operation return db.db.Delete(key, nil) } diff --git a/swarm/storage/database.go b/swarm/storage/database.go index 2532490cc9..f2ceb94e46 100644 --- a/swarm/storage/database.go +++ b/swarm/storage/database.go @@ -22,7 +22,6 @@ package storage import ( "fmt" - "github.com/ethereum/go-ethereum/compression/rle" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" @@ -31,8 +30,7 @@ import ( const openFileLimit = 128 type LDBDatabase struct { - db *leveldb.DB - comp bool + db *leveldb.DB } func NewLDBDatabase(file string) (*LDBDatabase, error) { @@ -42,16 +40,12 @@ func NewLDBDatabase(file string) (*LDBDatabase, error) { return nil, err } - database := &LDBDatabase{db: db, comp: false} + database := &LDBDatabase{db: db} return database, nil } func (self *LDBDatabase) Put(key []byte, value []byte) { - if self.comp { - value = rle.Compress(value) - } - err := self.db.Put(key, value, nil) if err != nil { fmt.Println("Error put", err) @@ -63,11 +57,6 @@ func (self *LDBDatabase) Get(key []byte) ([]byte, error) { if err != nil { return nil, err } - - if self.comp { - return rle.Decompress(dat) - } - return dat, nil } From 1100e8ba633d968da8ae2caca0a5d0cf48bcfa92 Mon Sep 17 00:00:00 2001 From: gary rong Date: Mon, 9 Apr 2018 20:46:27 +0800 Subject: [PATCH 17/36] eth/downloader: flush state sync data before exit (#16280) --- eth/downloader/statesync.go | 18 +++++++++++++----- trie/sync.go | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index 4071d0ad98..521ee25a02 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -274,15 +274,21 @@ func (s *stateSync) Cancel() error { // receive data from peers, rather those are buffered up in the downloader and // pushed here async. The reason is to decouple processing from data receipt // and timeouts. -func (s *stateSync) loop() error { +func (s *stateSync) loop() (err error) { // Listen for new peer events to assign tasks to them newPeer := make(chan *peerConnection, 1024) peerSub := s.d.peers.SubscribeNewPeers(newPeer) defer peerSub.Unsubscribe() + defer func() { + cerr := s.commit(true) + if err == nil { + err = cerr + } + }() // Keep assigning new tasks until the sync completes or aborts for s.sched.Pending() > 0 { - if err := s.commit(false); err != nil { + if err = s.commit(false); err != nil { return err } s.assignTasks() @@ -307,14 +313,14 @@ func (s *stateSync) loop() error { s.d.dropPeer(req.peer.id) } // Process all the received blobs and check for stale delivery - if err := s.process(req); err != nil { + if err = s.process(req); err != nil { log.Warn("Node data write error", "err", err) return err } req.peer.SetNodeDataIdle(len(req.response)) } } - return s.commit(true) + return nil } func (s *stateSync) commit(force bool) error { @@ -323,7 +329,9 @@ func (s *stateSync) commit(force bool) error { } start := time.Now() b := s.d.stateDB.NewBatch() - s.sched.Commit(b) + if written, err := s.sched.Commit(b); written == 0 || err != nil { + return err + } if err := b.Write(); err != nil { return fmt.Errorf("DB write error: %v", err) } diff --git a/trie/sync.go b/trie/sync.go index b573a9f732..4ae975d042 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -212,7 +212,7 @@ func (s *TrieSync) Process(results []SyncResult) (bool, int, error) { } // Commit flushes the data stored in the internal membatch out to persistent -// storage, returning th enumber of items written and any occurred error. +// storage, returning the number of items written and any occurred error. func (s *TrieSync) Commit(dbw ethdb.Putter) (int, error) { // Dump the membatch into a database dbw for i, key := range s.membatch.order { From 14c9215dd3afa7613c768a71baf40224df5e6aca Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 7 Apr 2018 19:14:20 +0200 Subject: [PATCH 18/36] state: handle nil in journal dirties --- core/state/statedb.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index c7ef49f64d..08f18c2d2f 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -517,7 +517,17 @@ func (self *StateDB) GetRefund() uint64 { // and clears the journal as well as the refunds. func (s *StateDB) Finalise(deleteEmptyObjects bool) { for addr := range s.journal.dirties { - stateObject := s.stateObjects[addr] + stateObject, exist := s.stateObjects[addr] + if !exist { + // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 + // That tx goes out of gas, and although the notion of 'touched' does not exist there, the + // touch-event will still be recorded in the journal. Since ripeMD is a special snowflake, + // it will persist in the journal even though the journal is reverted. In this special circumstance, + // it may exist in `s.journal.dirties` but not in `s.stateObjects`. + // Thus, we can safely ignore it here + continue + } + if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { s.deleteStateObject(stateObject) } else { From 8c31d2897ba6bf32097dffcd1bbe899172396533 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 7 Apr 2018 23:20:57 +0200 Subject: [PATCH 19/36] core: add blockchain benchmarks --- core/blockchain_test.go | 111 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 748cdc5c71..375823b57b 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1338,3 +1338,114 @@ func TestLargeReorgTrieGC(t *testing.T) { } } } + +// Benchmarks large blocks with value transfers to non-existing accounts +func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) { + var ( + signer = types.HomesteadSigner{} + testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + bankFunds = big.NewInt(100000000000000000) + gspec = Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{ + testBankAddress: {Balance: bankFunds}, + common.HexToAddress("0xc0de"): { + Code: []byte{0x60, 0x01, 0x50}, + Balance: big.NewInt(0), + }, // push 1, pop + }, + GasLimit: 100e6, // 100 M + } + ) + // Generate the original common chain segment and the two competing forks + engine := ethash.NewFaker() + db, _ := ethdb.NewMemDatabase() + genesis := gspec.MustCommit(db) + + blockGenerator := func(i int, block *BlockGen) { + block.SetCoinbase(common.Address{1}) + for txi := 0; txi < numTxs; txi++ { + uniq := uint64(i*numTxs + txi) + recipient := recipientFn(uniq) + //recipient := common.BigToAddress(big.NewInt(0).SetUint64(1337 + uniq)) + tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey) + if err != nil { + b.Error(err) + } + block.AddTx(tx) + } + } + + shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, numBlocks, blockGenerator) + b.StopTimer() + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Import the shared chain and the original canonical one + diskdb, _ := ethdb.NewMemDatabase() + gspec.MustCommit(diskdb) + + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) + if err != nil { + b.Fatalf("failed to create tester chain: %v", err) + } + b.StartTimer() + if _, err := chain.InsertChain(shared); err != nil { + b.Fatalf("failed to insert shared chain: %v", err) + } + b.StopTimer() + if got := chain.CurrentBlock().Transactions().Len(); got != numTxs*numBlocks { + b.Fatalf("Transactions were not included, expected %d, got %d", (numTxs * numBlocks), got) + + } + } +} +func BenchmarkBlockChain_1x1000ValueTransferToNonexisting(b *testing.B) { + var ( + numTxs = 1000 + numBlocks = 1 + ) + + recipientFn := func(nonce uint64) common.Address { + return common.BigToAddress(big.NewInt(0).SetUint64(1337 + nonce)) + } + dataFn := func(nonce uint64) []byte { + return nil + } + + benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn) +} +func BenchmarkBlockChain_1x1000ValueTransferToExisting(b *testing.B) { + var ( + numTxs = 1000 + numBlocks = 1 + ) + b.StopTimer() + b.ResetTimer() + + recipientFn := func(nonce uint64) common.Address { + return common.BigToAddress(big.NewInt(0).SetUint64(1337)) + } + dataFn := func(nonce uint64) []byte { + return nil + } + + benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn) +} +func BenchmarkBlockChain_1x1000Executions(b *testing.B) { + var ( + numTxs = 1000 + numBlocks = 1 + ) + b.StopTimer() + b.ResetTimer() + + recipientFn := func(nonce uint64) common.Address { + return common.BigToAddress(big.NewInt(0).SetUint64(0xc0de)) + } + dataFn := func(nonce uint64) []byte { + return nil + } + + benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn) +} From 7c1e9a5004c61ee6ec4636fd1926fafa0ae6a940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 10 Apr 2018 13:13:05 +0300 Subject: [PATCH 20/36] cmd/puppeth: fix node deploys for updated dockerfile user --- cmd/puppeth/module_node.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go index 2609fd976e..49c0991127 100644 --- a/cmd/puppeth/module_node.go +++ b/cmd/puppeth/module_node.go @@ -40,11 +40,11 @@ ADD genesis.json /genesis.json ADD signer.pass /signer.pass {{end}} RUN \ - echo 'geth --cache 512 init /genesis.json' > geth.sh && \{{if .Unlock}} - echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}} - echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh + echo 'geth --cache 512 init /genesis.json' > /root/geth.sh && \{{if .Unlock}} + echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> /root/geth.sh && \{{end}} + echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> /root/geth.sh -ENTRYPOINT ["/bin/sh", "geth.sh"] +ENTRYPOINT ["/bin/sh", "/root/geth.sh"] ` // nodeComposefile is the docker-compose.yml file required to deploy and maintain From 29213b1f8f00834bc1ff8d092ea26c970d5fc0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 10 Apr 2018 14:02:56 +0300 Subject: [PATCH 21/36] Dockerfile.alltools: fix invalid command --- Dockerfile.alltools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.alltools b/Dockerfile.alltools index 2175edbcb7..ec0ae92dd1 100644 --- a/Dockerfile.alltools +++ b/Dockerfile.alltools @@ -13,7 +13,7 @@ RUN apk add --no-cache ca-certificates COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/ RUN addgroup -g 1000 geth && \ - adduser -h /root -D -u 1000 -G geth geth \ + adduser -h /root -D -u 1000 -G geth geth && \ chown geth:geth /root USER geth From c7ab3e5544a3293819957281ecb7cfc08b4e9813 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Apr 2018 13:12:07 +0200 Subject: [PATCH 22/36] common: delete StringToAddress, StringToHash (#16436) * common: delete StringToAddress, StringToHash These functions are confusing because they don't parse hex, but use the bytes of the string. This change removes them, replacing all uses of StringToAddress(s) by BytesToAddress([]byte(s)). * eth/filters: remove incorrect use of common.BytesToAddress --- cmd/evm/runner.go | 4 ++-- common/types.go | 10 ++++------ core/vm/runtime/runtime.go | 4 ++-- eth/filters/api_test.go | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index c13e9fb335..2d9d31fb08 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -84,8 +84,8 @@ func runCmd(ctx *cli.Context) error { debugLogger *vm.StructLogger statedb *state.StateDB chainConfig *params.ChainConfig - sender = common.StringToAddress("sender") - receiver = common.StringToAddress("receiver") + sender = common.BytesToAddress([]byte("sender")) + receiver = common.BytesToAddress([]byte("receiver")) ) if ctx.GlobalBool(MachineFlag.Name) { tracer = NewJSONLogger(logconfig, os.Stdout) diff --git a/common/types.go b/common/types.go index fdc67480c2..4ea2d56a69 100644 --- a/common/types.go +++ b/common/types.go @@ -45,9 +45,8 @@ func BytesToHash(b []byte) Hash { h.SetBytes(b) return h } -func StringToHash(s string) Hash { return BytesToHash([]byte(s)) } -func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } -func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } +func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } +func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } // Get the string representation of the underlying hash func (h Hash) Str() string { return string(h[:]) } @@ -143,9 +142,8 @@ func BytesToAddress(b []byte) Address { a.SetBytes(b) return a } -func StringToAddress(s string) Address { return BytesToAddress([]byte(s)) } -func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } -func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) } +func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } +func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) } // IsHexAddress verifies whether a string can represent a valid hex-encoded // Ethereum address or not. diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 1e9ed7ae2d..5ac5464064 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -103,7 +103,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db)) } var ( - address = common.StringToAddress("contract") + address = common.BytesToAddress([]byte("contract")) vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) @@ -113,7 +113,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { // Call the code with the given configuration. ret, _, err := vmenv.Call( sender, - common.StringToAddress("contract"), + common.BytesToAddress([]byte("contract")), input, cfg.GasLimit, cfg.Value, diff --git a/eth/filters/api_test.go b/eth/filters/api_test.go index 4ae37f9779..02229a7549 100644 --- a/eth/filters/api_test.go +++ b/eth/filters/api_test.go @@ -29,8 +29,8 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) { var ( fromBlock rpc.BlockNumber = 0x123435 toBlock rpc.BlockNumber = 0xabcdef - address0 = common.StringToAddress("70c87d191324e6712a591f304b4eedef6ad9bb9d") - address1 = common.StringToAddress("9b2055d370f73ec7d8a03e965129118dc8f5bf83") + address0 = common.HexToAddress("70c87d191324e6712a591f304b4eedef6ad9bb9d") + address1 = common.HexToAddress("9b2055d370f73ec7d8a03e965129118dc8f5bf83") topic0 = common.HexToHash("3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1ca") topic1 = common.HexToHash("9084a792d2f8b16a62b882fd56f7860c07bf5fa91dd8a2ae7e809e5180fef0b3") topic2 = common.HexToHash("6ccae1c4af4152f460ff510e573399795dfab5dcf1fa60d1f33ac8fdc1e480ce") From 30deb6067fa5882ff6b2d0ead723eae833127490 Mon Sep 17 00:00:00 2001 From: ligi Date: Tue, 10 Apr 2018 13:32:48 +0200 Subject: [PATCH 23/36] build: add -e and -X flags to get more information on #16433 (#16443) --- build/ci.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ci.go b/build/ci.go index 1881a596e9..91de4e8769 100644 --- a/build/ci.go +++ b/build/ci.go @@ -766,7 +766,7 @@ func doAndroidArchive(cmdline []string) { if meta.Develop { repo = *deploy + "/content/repositories/snapshots" } - build.MustRunCommand("mvn", "gpg:sign-and-deploy-file", + build.MustRunCommand("mvn", "gpg:sign-and-deploy-file", "-e", "-X", "-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh", "-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar") } From 3caf16b15f0b6a30717acb245fa8d347b2f06c3f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Apr 2018 15:33:25 +0200 Subject: [PATCH 24/36] core: remove stray account creations in state transition (#16470) The 'from' and 'to' methods on StateTransitions are reader methods and shouldn't have inadvertent side effects on state. It is safe to remove the check in 'from' because account existence is implicitly checked by the nonce and balance checks. If the account has non-zero balance or nonce, it must exist. Even if the sender account has nonce zero at the start of the state transition or no balance, the nonce is incremented before execution and the account will be created at that time. It is safe to remove the check in 'to' because the EVM creates the account if necessary. Fixes #15119 --- core/state_transition.go | 58 +++++++++++----------------------------- tests/state_test.go | 1 - 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index b19bc12e42..5654cd01ea 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -132,28 +132,12 @@ func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, return NewStateTransition(evm, msg, gp).TransitionDb() } -func (st *StateTransition) from() vm.AccountRef { - f := st.msg.From() - if !st.state.Exist(f) { - st.state.CreateAccount(f) +// to returns the recipient of the message. +func (st *StateTransition) to() common.Address { + if st.msg == nil || st.msg.To() == nil /* contract creation */ { + return common.Address{} } - return vm.AccountRef(f) -} - -func (st *StateTransition) to() vm.AccountRef { - if st.msg == nil { - return vm.AccountRef{} - } - to := st.msg.To() - if to == nil { - return vm.AccountRef{} // contract creation - } - - reference := vm.AccountRef(*to) - if !st.state.Exist(*to) { - st.state.CreateAccount(*to) - } - return reference + return *st.msg.To() } func (st *StateTransition) useGas(amount uint64) error { @@ -166,12 +150,8 @@ func (st *StateTransition) useGas(amount uint64) error { } func (st *StateTransition) buyGas() error { - var ( - state = st.state - sender = st.from() - ) mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) - if state.GetBalance(sender.Address()).Cmp(mgval) < 0 { + if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 { return errInsufficientBalanceForGas } if err := st.gp.SubGas(st.msg.Gas()); err != nil { @@ -180,20 +160,17 @@ func (st *StateTransition) buyGas() error { st.gas += st.msg.Gas() st.initialGas = st.msg.Gas() - state.SubBalance(sender.Address(), mgval) + st.state.SubBalance(st.msg.From(), mgval) return nil } func (st *StateTransition) preCheck() error { - msg := st.msg - sender := st.from() - - // Make sure this transaction's nonce is correct - if msg.CheckNonce() { - nonce := st.state.GetNonce(sender.Address()) - if nonce < msg.Nonce() { + // Make sure this transaction's nonce is correct. + if st.msg.CheckNonce() { + nonce := st.state.GetNonce(st.msg.From()) + if nonce < st.msg.Nonce() { return ErrNonceTooHigh - } else if nonce > msg.Nonce() { + } else if nonce > st.msg.Nonce() { return ErrNonceTooLow } } @@ -208,8 +185,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo return } msg := st.msg - sender := st.from() // err checked in preCheck - + sender := vm.AccountRef(msg.From()) homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) contractCreation := msg.To() == nil @@ -233,8 +209,8 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) } else { // Increment the nonce for the next transaction - st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1) - ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value) + st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) + ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value) } if vmerr != nil { log.Debug("VM returned with error", "err", vmerr) @@ -260,10 +236,8 @@ func (st *StateTransition) refundGas() { st.gas += refund // Return ETH for remaining gas, exchanged at the original rate. - sender := st.from() - remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) - st.state.AddBalance(sender.Address(), remaining) + st.state.AddBalance(st.msg.From(), remaining) // Also return remaining gas to the block gas counter so it is // available for the next transaction. diff --git a/tests/state_test.go b/tests/state_test.go index 3fd3ce43ab..adec4feb2b 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -37,7 +37,6 @@ func TestState(t *testing.T) { // Expected failures: st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/EIP158`, "bug in test") st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/Byzantium`, "bug in test") - st.fails(`^stRandom2/randomStatetest64[45]\.json/(EIP150|Frontier|Homestead)/.*`, "known bug #15119") st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) { for _, subtest := range test.Subtests() { From 95d5c2208644aa7d9a3cb7e1585a9bf05f212c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 10 Apr 2018 17:07:58 +0300 Subject: [PATCH 25/36] travis, appveyor: bump to Go 1.10.1 --- .travis.yml | 18 +++++++++--------- appveyor.yml | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 40d940de0d..e64f7a8eb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ matrix: - os: linux dist: trusty sudo: required - go: "1.10" + go: 1.10.x script: - sudo modprobe fuse - sudo chmod 666 /dev/fuse @@ -27,7 +27,7 @@ matrix: - go run build/ci.go test -coverage - os: osx - go: "1.10" + go: 1.10.x script: - unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703 - brew update @@ -39,7 +39,7 @@ matrix: # This builder only tests code linters on latest version of Go - os: linux dist: trusty - go: "1.10" + go: 1.10.x env: - lint git: @@ -50,7 +50,7 @@ matrix: # This builder does the Ubuntu PPA upload - os: linux dist: trusty - go: "1.10" + go: 1.10.x env: - ubuntu-ppa git: @@ -69,7 +69,7 @@ matrix: - os: linux dist: trusty sudo: required - go: "1.10" + go: 1.10.x env: - azure-linux git: @@ -103,7 +103,7 @@ matrix: dist: trusty services: - docker - go: "1.10" + go: 1.10.x env: - azure-linux-mips git: @@ -147,7 +147,7 @@ matrix: git: submodules: false # avoid cloning ethereum/tests before_install: - - curl https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz | tar -xz + - curl https://storage.googleapis.com/golang/go1.10.1.linux-amd64.tar.gz | tar -xz - export PATH=`pwd`/go/bin:$PATH - export GOROOT=`pwd`/go - export GOPATH=$HOME/go @@ -164,7 +164,7 @@ matrix: # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads - os: osx - go: "1.10" + go: 1.10.x env: - azure-osx - azure-ios @@ -193,7 +193,7 @@ matrix: # This builder does the Azure archive purges to avoid accumulating junk - os: linux dist: trusty - go: "1.10" + go: 1.10.x env: - azure-purge git: diff --git a/appveyor.yml b/appveyor.yml index 45475d1669..141ef16ff8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,8 +23,8 @@ environment: install: - git submodule update --init - rmdir C:\go /s /q - - appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.windows-%GETH_ARCH%.zip - - 7z x go1.10.windows-%GETH_ARCH%.zip -y -oC:\ > NUL + - appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.1.windows-%GETH_ARCH%.zip + - 7z x go1.10.1.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - go version - gcc --version From 2e247705cd27e919e806f29ff2adcd57c163b1ac Mon Sep 17 00:00:00 2001 From: Elad_ Date: Tue, 10 Apr 2018 16:35:26 +0200 Subject: [PATCH 26/36] travis.yml: add TEST_PACKAGES to speed up swarm testing (#16456) This commit is meant to allow ecosystem projects such as ethersphere to minimize CI build times by specifying an environment variable with the packages to run tests on. If the environment variable isn't defined the build script will test all packages so this shouldn't affect the main go-ethereum repository. --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 40d940de0d..1874d6cb9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ matrix: - sudo chmod 666 /dev/fuse - sudo chown root:$USER /etc/fuse.conf - go run build/ci.go install - - go run build/ci.go test -coverage + - go run build/ci.go test -coverage $TEST_PACKAGES # These are the latest Go versions. - os: linux @@ -24,7 +24,7 @@ matrix: - sudo chmod 666 /dev/fuse - sudo chown root:$USER /etc/fuse.conf - go run build/ci.go install - - go run build/ci.go test -coverage + - go run build/ci.go test -coverage $TEST_PACKAGES - os: osx go: "1.10" @@ -34,7 +34,7 @@ matrix: - brew install caskroom/cask/brew-cask - brew cask install osxfuse - go run build/ci.go install - - go run build/ci.go test -coverage + - go run build/ci.go test -coverage $TEST_PACKAGES # This builder only tests code linters on latest version of Go - os: linux From e7cc5b41605f9145b2cb5755476456f30974a2c8 Mon Sep 17 00:00:00 2001 From: cpusoft Date: Wed, 11 Apr 2018 16:02:33 +0800 Subject: [PATCH 27/36] les: add ps.lock.Unlock() before return (#16360) --- les/peer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/les/peer.go b/les/peer.go index caf5680778..eb7452e276 100644 --- a/les/peer.go +++ b/les/peer.go @@ -545,9 +545,11 @@ func (ps *peerSet) notify(n peerSetNotify) { func (ps *peerSet) Register(p *peer) error { ps.lock.Lock() if ps.closed { + ps.lock.Unlock() return errClosed } if _, ok := ps.peers[p.id]; ok { + ps.lock.Unlock() return errAlreadyRegistered } ps.peers[p.id] = p From 0c7b99b8cc754a0045518ac12b8d1719c23b4181 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 10 Apr 2018 20:59:07 +0200 Subject: [PATCH 28/36] core/state: fix bug in copy of copy State --- core/state/statedb.go | 10 ++++++++++ core/state/statedb_test.go | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/core/state/statedb.go b/core/state/statedb.go index 08f18c2d2f..97c6e4a017 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -474,6 +474,16 @@ func (self *StateDB) Copy() *StateDB { state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) state.stateObjectsDirty[addr] = struct{}{} } + // Above, we don't copy the actual journal. This means that if the copy is copied, the + // loop above will be a no-op, since the copy's journal is empty. + // Thus, here we iterate over stateObjects, to enable copies of copies + for addr := range self.stateObjectsDirty { + if _, exist := state.stateObjects[addr]; !exist { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) + state.stateObjectsDirty[addr] = struct{}{} + } + } + for hash, logs := range self.logs { state.logs[hash] = make([]*types.Log, len(logs)) copy(state.logs[hash], logs) diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 340c840f13..420ca745cc 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -422,3 +422,19 @@ func (s *StateSuite) TestTouchDelete(c *check.C) { c.Fatal("expected no dirty state object") } } + +// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. +// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512 +func TestCopyOfCopy(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + sdb, _ := New(common.Hash{}, NewDatabase(db)) + addr := common.HexToAddress("aaaa") + sdb.SetBalance(addr, big.NewInt(42)) + + if got := sdb.Copy().GetBalance(addr).Uint64(); got != 42 { + t.Fatalf("1st copy fail, expected 42, got %v", got) + } + if got := sdb.Copy().Copy().GetBalance(addr).Uint64(); got != 42 { + t.Fatalf("2nd copy fail, expected 42, got %v", got) + } +} From 7205366c9f85bbfcf86ac78efcd005bb10dfc5fe Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 11 Apr 2018 15:03:49 +0200 Subject: [PATCH 29/36] core/state: fix ripemd-cornercase in Copy --- core/state/statedb.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 97c6e4a017..3ae6843d87 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -471,8 +471,14 @@ func (self *StateDB) Copy() *StateDB { } // Copy the dirty states, logs, and preimages for addr := range self.journal.dirties { - state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) - state.stateObjectsDirty[addr] = struct{}{} + // As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527), + // and in the Finalise-method, there is a case where an object is in the journal but not + // in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for + // nil + if object, exist := self.stateObjects[addr]; exist { + state.stateObjects[addr] = object.deepCopy(state) + state.stateObjectsDirty[addr] = struct{}{} + } } // Above, we don't copy the actual journal. This means that if the copy is copied, the // loop above will be a no-op, since the copy's journal is empty. From db48d312e44dab3a756aa6976412fe54f8e891ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 12 Apr 2018 12:17:52 +0300 Subject: [PATCH 30/36] core: txpool stable underprice drop order, perf fixes --- core/tx_list.go | 17 +++++++-- core/tx_pool.go | 23 ++++++------ core/tx_pool_test.go | 85 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 101 insertions(+), 24 deletions(-) diff --git a/core/tx_list.go b/core/tx_list.go index 55fc42617d..ea6ee7019f 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -367,9 +367,20 @@ func (l *txList) Flatten() types.Transactions { // price-sorted transactions to discard when the pool fills up. type priceHeap []*types.Transaction -func (h priceHeap) Len() int { return len(h) } -func (h priceHeap) Less(i, j int) bool { return h[i].GasPrice().Cmp(h[j].GasPrice()) < 0 } -func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h priceHeap) Len() int { return len(h) } +func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h priceHeap) Less(i, j int) bool { + // Sort primarily by price, returning the cheaper one + switch h[i].GasPrice().Cmp(h[j].GasPrice()) { + case -1: + return true + case 1: + return false + } + // If the prices match, stabilize via nonces (high nonce is worse) + return h[i].Nonce() > h[j].Nonce() +} func (h *priceHeap) Push(x interface{}) { *h = append(*h, x.(*types.Transaction)) diff --git a/core/tx_pool.go b/core/tx_pool.go index 089bd215ad..a554f66117 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -320,7 +320,7 @@ func (pool *TxPool) loop() { // Any non-locals old enough should be removed if time.Since(pool.beats[addr]) > pool.config.Lifetime { for _, tx := range pool.queue[addr].Flatten() { - pool.removeTx(tx.Hash()) + pool.removeTx(tx.Hash(), true) } } } @@ -468,7 +468,7 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { pool.gasPrice = price for _, tx := range pool.priced.Cap(price, pool.locals) { - pool.removeTx(tx.Hash()) + pool.removeTx(tx.Hash(), false) } log.Info("Transaction pool price threshold updated", "price", price) } @@ -630,7 +630,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) { for _, tx := range drop { log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice()) underpricedTxCounter.Inc(1) - pool.removeTx(tx.Hash()) + pool.removeTx(tx.Hash(), false) } } // If the transaction is replacing an already pending one, do directly @@ -695,8 +695,10 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er pool.priced.Removed() queuedReplaceCounter.Inc(1) } - pool.all[hash] = tx - pool.priced.Put(tx) + if pool.all[hash] == nil { + pool.all[hash] = tx + pool.priced.Put(tx) + } return old != nil, nil } @@ -862,7 +864,7 @@ func (pool *TxPool) Get(hash common.Hash) *types.Transaction { // removeTx removes a single transaction from the queue, moving all subsequent // transactions back to the future queue. -func (pool *TxPool) removeTx(hash common.Hash) { +func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { // Fetch the transaction we wish to delete tx, ok := pool.all[hash] if !ok { @@ -872,8 +874,9 @@ func (pool *TxPool) removeTx(hash common.Hash) { // Remove it from the list of known transactions delete(pool.all, hash) - pool.priced.Removed() - + if outofbound { + pool.priced.Removed() + } // Remove the transaction from the pending lists and reset the account nonce if pending := pool.pending[addr]; pending != nil { if removed, invalids := pending.Remove(tx); removed { @@ -1052,7 +1055,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) { // Drop all transactions if they are less than the overflow if size := uint64(list.Len()); size <= drop { for _, tx := range list.Flatten() { - pool.removeTx(tx.Hash()) + pool.removeTx(tx.Hash(), true) } drop -= size queuedRateLimitCounter.Inc(int64(size)) @@ -1061,7 +1064,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) { // Otherwise drop only last few transactions txs := list.Flatten() for i := len(txs) - 1; i >= 0 && drop > 0; i-- { - pool.removeTx(txs[i].Hash()) + pool.removeTx(txs[i].Hash(), true) drop-- queuedRateLimitCounter.Inc(1) } diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 1cf533aa65..0cb14cb6a4 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -209,15 +209,10 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { pool.lockedReset(nil, nil) - pendingTx, err := pool.Pending() + _, err := pool.Pending() if err != nil { t.Fatalf("Could not fetch pending transactions: %v", err) } - - for addr, txs := range pendingTx { - t.Logf("%0x: %d\n", addr, len(txs)) - } - nonce = pool.State().GetNonce(address) if nonce != 2 { t.Fatalf("Invalid nonce, want 2, got %d", nonce) @@ -350,7 +345,7 @@ func TestTransactionChainFork(t *testing.T) { if _, err := pool.add(tx, false); err != nil { t.Error("didn't expect error", err) } - pool.removeTx(tx.Hash()) + pool.removeTx(tx.Hash(), true) // reset the pool's internal state resetState() @@ -1388,13 +1383,13 @@ func TestTransactionPoolUnderpricing(t *testing.T) { t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced) } // Ensure that adding high priced transactions drops cheap ones, but not own - if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil { + if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil { // +K1:0 => -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que - t.Fatalf("failed to add well priced transaction: %v", err) } - if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(4), keys[1])); err != nil { + if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(4), keys[1])); err != nil { // +K1:2 => -K0:0 => Pend K1:0, K2:0; Que K0:1 K1:2 t.Fatalf("failed to add well priced transaction: %v", err) } - if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(5), keys[1])); err != nil { + if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(5), keys[1])); err != nil { // +K1:3 => -K0:1 => Pend K1:0, K2:0; Que K1:2 K1:3 t.Fatalf("failed to add well priced transaction: %v", err) } pending, queued = pool.Stats() @@ -1404,7 +1399,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) } - if err := validateEvents(events, 2); err != nil { + if err := validateEvents(events, 1); err != nil { t.Fatalf("additional event firing failed: %v", err) } if err := validateTxPoolInternals(pool); err != nil { @@ -1430,6 +1425,74 @@ func TestTransactionPoolUnderpricing(t *testing.T) { } } +// Tests that more expensive transactions push out cheap ones from the pool, but +// without producing instability by creating gaps that start jumping transactions +// back and forth between queued/pending. +func TestTransactionPoolStableUnderpricing(t *testing.T) { + t.Parallel() + + // Create the pool to test the pricing enforcement with + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) + blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} + + config := testTxPoolConfig + config.GlobalSlots = 128 + config.GlobalQueue = 0 + + pool := NewTxPool(config, params.TestChainConfig, blockchain) + defer pool.Stop() + + // Keep track of transaction events to ensure all executables get announced + events := make(chan TxPreEvent, 32) + sub := pool.txFeed.Subscribe(events) + defer sub.Unsubscribe() + + // Create a number of test accounts and fund them + keys := make([]*ecdsa.PrivateKey, 2) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + } + // Fill up the entire queue with the same transaction price points + txs := types.Transactions{} + for i := uint64(0); i < config.GlobalSlots; i++ { + txs = append(txs, pricedTransaction(i, 100000, big.NewInt(1), keys[0])) + } + pool.AddRemotes(txs) + + pending, queued := pool.Stats() + if pending != int(config.GlobalSlots) { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots) + } + if queued != 0 { + t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) + } + if err := validateEvents(events, int(config.GlobalSlots)); err != nil { + t.Fatalf("original event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } + // Ensure that adding high priced transactions drops a cheap, but doesn't produce a gap + if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil { + t.Fatalf("failed to add well priced transaction: %v", err) + } + pending, queued = pool.Stats() + if pending != int(config.GlobalSlots) { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots) + } + if queued != 0 { + t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) + } + if err := validateEvents(events, 1); err != nil { + t.Fatalf("additional event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } +} + // Tests that the pool rejects replacement transactions that don't meet the minimum // price bump required. func TestTransactionReplacement(t *testing.T) { From 2a1fc3d155870ba2e42f108a232b4b6a3ad9d939 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Mon, 16 Apr 2018 00:56:20 -0700 Subject: [PATCH 31/36] miner: remove contention on currentMu for pending data retrievals (#16497) --- miner/worker.go | 51 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 15395ae0b9..48b0b27652 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -117,6 +117,10 @@ type worker struct { currentMu sync.Mutex current *Work + snapshotMu sync.RWMutex + snapshotBlock *types.Block + snapshotState *state.StateDB + uncleMu sync.Mutex possibleUncles map[common.Hash]*types.Block @@ -171,32 +175,28 @@ func (self *worker) setExtra(extra []byte) { } func (self *worker) pending() (*types.Block, *state.StateDB) { + if atomic.LoadInt32(&self.mining) == 0 { + // return a snapshot to avoid contention on currentMu mutex + self.snapshotMu.RLock() + defer self.snapshotMu.RUnlock() + return self.snapshotBlock, self.snapshotState.Copy() + } + self.currentMu.Lock() defer self.currentMu.Unlock() - - if atomic.LoadInt32(&self.mining) == 0 { - return types.NewBlock( - self.current.header, - self.current.txs, - nil, - self.current.receipts, - ), self.current.state.Copy() - } return self.current.Block, self.current.state.Copy() } func (self *worker) pendingBlock() *types.Block { + if atomic.LoadInt32(&self.mining) == 0 { + // return a snapshot to avoid contention on currentMu mutex + self.snapshotMu.RLock() + defer self.snapshotMu.RUnlock() + return self.snapshotBlock + } + self.currentMu.Lock() defer self.currentMu.Unlock() - - if atomic.LoadInt32(&self.mining) == 0 { - return types.NewBlock( - self.current.header, - self.current.txs, - nil, - self.current.receipts, - ) - } return self.current.Block } @@ -268,6 +268,7 @@ func (self *worker) update() { txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs) self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase) + self.updateSnapshot() self.currentMu.Unlock() } else { // If we're mining, but nothing is being processed, wake on new transactions @@ -489,6 +490,7 @@ func (self *worker) commitNewWork() { self.unconfirmed.Shift(work.Block.NumberU64() - 1) } self.push(work) + self.updateSnapshot() } func (self *worker) commitUncle(work *Work, uncle *types.Header) error { @@ -506,6 +508,19 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error { return nil } +func (self *worker) updateSnapshot() { + self.snapshotMu.Lock() + defer self.snapshotMu.Unlock() + + self.snapshotBlock = types.NewBlock( + self.current.header, + self.current.txs, + nil, + self.current.receipts, + ) + self.snapshotState = self.current.state.Copy() +} + func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) { gp := new(core.GasPool).AddGas(env.header.GasLimit) From 6b2b328cdb51a99e2ce55f0bc22eeb9733c9ec30 Mon Sep 17 00:00:00 2001 From: gary rong Date: Mon, 16 Apr 2018 16:18:24 +0800 Subject: [PATCH 32/36] ethdb: add leveldb write delay statistic (#16499) --- ethdb/database.go | 95 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/ethdb/database.go b/ethdb/database.go index 243b7f8d3c..001d8f0bb9 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -17,6 +17,7 @@ package ethdb import ( + "fmt" "strconv" "strings" "sync" @@ -32,17 +33,25 @@ import ( "github.com/syndtr/goleveldb/leveldb/util" ) +const ( + writeDelayNThreshold = 200 + writeDelayThreshold = 350 * time.Millisecond + writeDelayWarningThrottler = 1 * time.Minute +) + var OpenFileLimit = 64 type LDBDatabase struct { fn string // filename for reporting db *leveldb.DB // LevelDB instance - compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction - compReadMeter metrics.Meter // Meter for measuring the data read during compaction - compWriteMeter metrics.Meter // Meter for measuring the data written during compaction - diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read - diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written + compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction + compReadMeter metrics.Meter // Meter for measuring the data read during compaction + compWriteMeter metrics.Meter // Meter for measuring the data written during compaction + writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction + writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction + diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read + diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written quitLock sync.Mutex // Mutex protecting the quit channel access quitChan chan chan error // Quit channel to stop the metrics collection before closing the database @@ -147,16 +156,17 @@ func (db *LDBDatabase) LDB() *leveldb.DB { // Meter configures the database metrics collectors and func (db *LDBDatabase) Meter(prefix string) { - // Short circuit metering if the metrics system is disabled - if !metrics.Enabled { - return + if metrics.Enabled { + // Initialize all the metrics collector at the requested prefix + db.compTimeMeter = metrics.NewRegisteredMeter(prefix+"compact/time", nil) + db.compReadMeter = metrics.NewRegisteredMeter(prefix+"compact/input", nil) + db.compWriteMeter = metrics.NewRegisteredMeter(prefix+"compact/output", nil) + db.diskReadMeter = metrics.NewRegisteredMeter(prefix+"disk/read", nil) + db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil) } - // Initialize all the metrics collector at the requested prefix - db.compTimeMeter = metrics.NewRegisteredMeter(prefix+"compact/time", nil) - db.compReadMeter = metrics.NewRegisteredMeter(prefix+"compact/input", nil) - db.compWriteMeter = metrics.NewRegisteredMeter(prefix+"compact/output", nil) - db.diskReadMeter = metrics.NewRegisteredMeter(prefix+"disk/read", nil) - db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil) + // Initialize write delay metrics no matter we are in metric mode or not. + db.writeDelayMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/duration", nil) + db.writeDelayNMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/counter", nil) // Create a quit channel for the periodic collector and run it db.quitLock.Lock() @@ -178,6 +188,9 @@ func (db *LDBDatabase) Meter(prefix string) { // 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884 // 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000 // +// This is how the write delay look like (currently): +// DelayN:5 Delay:406.604657ms +// // This is how the iostats look like (currently): // Read(MB):3895.04860 Write(MB):3654.64712 func (db *LDBDatabase) meter(refresh time.Duration) { @@ -188,6 +201,14 @@ func (db *LDBDatabase) meter(refresh time.Duration) { } // Create storage for iostats. var iostats [2]float64 + + // Create storage and warning log tracer for write delay. + var ( + delaystats [2]int64 + lastWriteDelay time.Time + lastWriteDelayN time.Time + ) + // Iterate ad infinitum and collect the stats for i := 1; ; i++ { // Retrieve the database stats @@ -236,6 +257,52 @@ func (db *LDBDatabase) meter(refresh time.Duration) { db.compWriteMeter.Mark(int64((compactions[i%2][2] - compactions[(i-1)%2][2]) * 1024 * 1024)) } + // Retrieve the write delay statistic + writedelay, err := db.db.GetProperty("leveldb.writedelay") + if err != nil { + db.log.Error("Failed to read database write delay statistic", "err", err) + return + } + var ( + delayN int64 + delayDuration string + duration time.Duration + ) + if n, err := fmt.Sscanf(writedelay, "DelayN:%d Delay:%s", &delayN, &delayDuration); n != 2 || err != nil { + db.log.Error("Write delay statistic not found") + return + } + duration, err = time.ParseDuration(delayDuration) + if err != nil { + db.log.Error("Failed to parse delay duration", "err", err) + return + } + if db.writeDelayNMeter != nil { + db.writeDelayNMeter.Mark(delayN - delaystats[0]) + // If the write delay number been collected in the last minute exceeds the predefined threshold, + // print a warning log here. + // If a warning that db performance is laggy has been displayed, + // any subsequent warnings will be withhold for 1 minute to don't overwhelm the user. + if int(db.writeDelayNMeter.Rate1()) > writeDelayNThreshold && + time.Now().After(lastWriteDelayN.Add(writeDelayWarningThrottler)) { + db.log.Warn("Write delay number exceeds the threshold (200 per second) in the last minute") + lastWriteDelayN = time.Now() + } + } + if db.writeDelayMeter != nil { + db.writeDelayMeter.Mark(duration.Nanoseconds() - delaystats[1]) + // If the write delay duration been collected in the last minute exceeds the predefined threshold, + // print a warning log here. + // If a warning that db performance is laggy has been displayed, + // any subsequent warnings will be withhold for 1 minute to don't overwhelm the user. + if int64(db.writeDelayMeter.Rate1()) > writeDelayThreshold.Nanoseconds() && + time.Now().After(lastWriteDelay.Add(writeDelayWarningThrottler)) { + db.log.Warn("Write delay duration exceeds the threshold (35% of the time) in the last minute") + lastWriteDelay = time.Now() + } + } + delaystats[0], delaystats[1] = delayN, duration.Nanoseconds() + // Retrieve the database iostats. ioStats, err := db.db.GetProperty("leveldb.iostats") if err != nil { From de2a7bb764c82dbaa80d37939c5862358174bc6e Mon Sep 17 00:00:00 2001 From: gary rong Date: Mon, 16 Apr 2018 16:37:48 +0800 Subject: [PATCH 33/36] eth/downloader: wait for all fetcher goroutines to exit before terminating (#16509) --- eth/downloader/downloader.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 9e49498998..8b181b8ad4 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -135,9 +135,10 @@ type Downloader struct { stateCh chan dataPack // [eth/63] Channel receiving inbound node state data // Cancellation and termination - cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) - cancelCh chan struct{} // Channel to cancel mid-flight syncs - cancelLock sync.RWMutex // Lock to protect the cancel channel and peer in delivers + cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) + cancelCh chan struct{} // Channel to cancel mid-flight syncs + cancelLock sync.RWMutex // Lock to protect the cancel channel and peer in delivers + cancelWg sync.WaitGroup // Make sure all fetcher goroutines have exited. quitCh chan struct{} // Quit channel to signal termination quitLock sync.RWMutex // Lock to prevent double closes @@ -476,12 +477,11 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I // spawnSync runs d.process and all given fetcher functions to completion in // separate goroutines, returning the first error that appears. func (d *Downloader) spawnSync(fetchers []func() error) error { - var wg sync.WaitGroup errc := make(chan error, len(fetchers)) - wg.Add(len(fetchers)) + d.cancelWg.Add(len(fetchers)) for _, fn := range fetchers { fn := fn - go func() { defer wg.Done(); errc <- fn() }() + go func() { defer d.cancelWg.Done(); errc <- fn() }() } // Wait for the first error, then terminate the others. var err error @@ -498,12 +498,10 @@ func (d *Downloader) spawnSync(fetchers []func() error) error { } d.queue.Close() d.Cancel() - wg.Wait() return err } -// Cancel cancels all of the operations and resets the queue. It returns true -// if the cancel operation was completed. +// Cancel cancels all of the operations and resets the queue. func (d *Downloader) Cancel() { // Close the current cancel channel d.cancelLock.Lock() @@ -516,6 +514,7 @@ func (d *Downloader) Cancel() { } } d.cancelLock.Unlock() + d.cancelWg.Wait() } // Terminate interrupts the downloader, canceling all pending operations. From ec3db0f56c779387132dcf2049ed32bf4ed34a4f Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 16 Apr 2018 14:04:32 +0200 Subject: [PATCH 34/36] cmd/clef, signer: initial poc of the standalone signer (#16154) * signer: introduce external signer command * cmd/signer, rpc: Implement new signer. Add info about remote user to Context * signer: refactored request/response, made use of urfave.cli * cmd/signer: Use common flags * cmd/signer: methods to validate calldata against abi * cmd/signer: work on abi parser * signer: add mutex around UI * cmd/signer: add json 4byte directory, remove passwords from api * cmd/signer: minor changes * cmd/signer: Use ErrRequestDenied, enable lightkdf * cmd/signer: implement tests * cmd/signer: made possible for UI to modify tx parameters * cmd/signer: refactors, removed channels in ui comms, added UI-api via stdin/out * cmd/signer: Made lowercase json-definitions, added UI-signer test functionality * cmd/signer: update documentation * cmd/signer: fix bugs, improve abi detection, abi argument display * cmd/signer: minor change in json format * cmd/signer: rework json communication * cmd/signer: implement mixcase addresses in API, fix json id bug * cmd/signer: rename fromaccount, update pythonpoc with new json encoding format * cmd/signer: make use of new abi interface * signer: documentation * signer/main: remove redundant option * signer: implement audit logging * signer: create package 'signer', minor changes * common: add 0x-prefix to mixcaseaddress in json marshalling + validation * signer, rules, storage: implement rules + ephemeral storage for signer rules * signer: implement OnApprovedTx, change signing response (API BREAKAGE) * signer: refactoring + documentation * signer/rules: implement dispatching to next handler * signer: docs * signer/rules: hide json-conversion from users, ensure context is cleaned * signer: docs * signer: implement validation rules, change signature of call_info * signer: fix log flaw with string pointer * signer: implement custom 4byte databsae that saves submitted signatures * signer/storage: implement aes-gcm-backed credential storage * accounts: implement json unmarshalling of url * signer: fix listresponse, fix gas->uint64 * node: make http/ipc start methods public * signer: add ipc capability+review concerns * accounts: correct docstring * signer: address review concerns * rpc: go fmt -s * signer: review concerns+ baptize Clef * signer,node: move Start-functions to separate file * signer: formatting --- accounts/url.go | 16 + accounts/usbwallet/hub.go | 2 +- accounts/usbwallet/wallet.go | 2 +- cmd/clef/4byte.json | 1 + cmd/clef/README.md | 864 +++++++++++++++++++++++++ cmd/clef/extapi_changelog.md | 25 + cmd/clef/intapi_changelog.md | 86 +++ cmd/clef/main.go | 640 ++++++++++++++++++ cmd/clef/pythonsigner.py | 179 +++++ cmd/clef/rules.md | 236 +++++++ cmd/clef/sign_flow.png | Bin 0 -> 36397 bytes cmd/clef/tutorial.md | 198 ++++++ common/types.go | 62 ++ common/types_test.go | 44 ++ node/node.go | 91 +-- rpc/client.go | 53 +- rpc/endpoints.go | 120 ++++ rpc/http.go | 7 +- rpc/server.go | 11 +- signer/core/abihelper.go | 256 ++++++++ signer/core/abihelper_test.go | 247 +++++++ signer/core/api.go | 500 ++++++++++++++ signer/core/api_test.go | 386 +++++++++++ signer/core/auditlog.go | 110 ++++ signer/core/cliui.go | 247 +++++++ signer/core/stdioui.go | 113 ++++ signer/core/types.go | 95 +++ signer/core/validation.go | 163 +++++ signer/core/validation_test.go | 139 ++++ signer/rules/deps/bignumber.js | 4 + signer/rules/deps/bindata.go | 235 +++++++ signer/rules/deps/deps.go | 21 + signer/rules/rules.go | 248 +++++++ signer/rules/rules_test.go | 631 ++++++++++++++++++ signer/storage/aes_gcm_storage.go | 164 +++++ signer/storage/aes_gcm_storage_test.go | 115 ++++ signer/storage/storage.go | 62 ++ 37 files changed, 6281 insertions(+), 92 deletions(-) create mode 100644 cmd/clef/4byte.json create mode 100644 cmd/clef/README.md create mode 100644 cmd/clef/extapi_changelog.md create mode 100644 cmd/clef/intapi_changelog.md create mode 100644 cmd/clef/main.go create mode 100644 cmd/clef/pythonsigner.py create mode 100644 cmd/clef/rules.md create mode 100644 cmd/clef/sign_flow.png create mode 100644 cmd/clef/tutorial.md create mode 100644 rpc/endpoints.go create mode 100644 signer/core/abihelper.go create mode 100644 signer/core/abihelper_test.go create mode 100644 signer/core/api.go create mode 100644 signer/core/api_test.go create mode 100644 signer/core/auditlog.go create mode 100644 signer/core/cliui.go create mode 100644 signer/core/stdioui.go create mode 100644 signer/core/types.go create mode 100644 signer/core/validation.go create mode 100644 signer/core/validation_test.go create mode 100644 signer/rules/deps/bignumber.js create mode 100644 signer/rules/deps/bindata.go create mode 100644 signer/rules/deps/deps.go create mode 100644 signer/rules/rules.go create mode 100644 signer/rules/rules_test.go create mode 100644 signer/storage/aes_gcm_storage.go create mode 100644 signer/storage/aes_gcm_storage_test.go create mode 100644 signer/storage/storage.go diff --git a/accounts/url.go b/accounts/url.go index 47f9d8ee4b..21df668efd 100644 --- a/accounts/url.go +++ b/accounts/url.go @@ -74,6 +74,22 @@ func (u URL) MarshalJSON() ([]byte, error) { return json.Marshal(u.String()) } +// UnmarshalJSON parses url. +func (u *URL) UnmarshalJSON(input []byte) error { + var textUrl string + err := json.Unmarshal(input, &textUrl) + if err != nil { + return err + } + url, err := parseURL(textUrl) + if err != nil { + return err + } + u.Scheme = url.Scheme + u.Path = url.Path + return nil +} + // Cmp compares x and y and returns: // // -1 if x < y diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go index 61fc98ccc8..640320bc91 100644 --- a/accounts/usbwallet/hub.go +++ b/accounts/usbwallet/hub.go @@ -127,7 +127,7 @@ func (hub *Hub) refreshWallets() { // breaking the Ledger protocol if that is waiting for user confirmation. This // is a bug acknowledged at Ledger, but it won't be fixed on old devices so we // need to prevent concurrent comms ourselves. The more elegant solution would - // be to ditch enumeration in favor of hutplug events, but that don't work yet + // be to ditch enumeration in favor of hotplug events, but that don't work yet // on Windows so if we need to hack it anyway, this is more elegant for now. hub.commsLock.Lock() if hub.commsPend > 0 { // A confirmation is pending, don't refresh diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go index 8b3b5a5224..6cef6e0fb0 100644 --- a/accounts/usbwallet/wallet.go +++ b/accounts/usbwallet/wallet.go @@ -99,7 +99,7 @@ type wallet struct { // // As such, a hardware wallet needs two locks to function correctly. A state // lock can be used to protect the wallet's software-side internal state, which - // must not be held exlusively during hardware communication. A communication + // must not be held exclusively during hardware communication. A communication // lock can be used to achieve exclusive access to the device itself, this one // however should allow "skipping" waiting for operations that might want to // use the device, but can live without too (e.g. account self-derivation). diff --git a/cmd/clef/4byte.json b/cmd/clef/4byte.json new file mode 100644 index 0000000000..5603d5931d --- /dev/null +++ b/cmd/clef/4byte.json @@ -0,0 +1 @@ +{"0x22ec1244": "shaBid(bytes32,address,uint256,bytes32)", "0xcae9ca51": "approveAndCall(address,uint256,bytes)", "0x4fb2e45d": "transferOwner(address)", "0x7741b4ec": "RandomNumberFromSeed(uint256)", "0x267127ec": "getTokenSettings()", "0xb7213bd4": "readLog(uint256)", "0x3018205f": "getController()", "0xc8edf65e": "GetAndReduceFeesByFraction(uint256)", "0xeec2b628": "beforeExecute(address)", "0xfc0c546a": "token()", "0x40a3d246": "toggle()", "0x70983e91": "startBoardProposal(uint256,address)", "0x6b5caec4": "setBot(address)", "0x78524b2e": "halveMinQuorum()", "0x2c60a055": "MapTest()", "0xc2fb8f36": "TinyHuman(address,address,address)", "0x6822abae": "getMinimumCallCost(uint256)", "0x6f9fb98a": "getContractBalance()", "0x5c17f9f4": "approve(address,uint256,bytes)", "0x504ac982": "transfer(string,string)", "0x06e53f47": "whichChainIsThis()", "0xf359671c": "withdrawWithReference(address,uint256,string)", "0xf97d0591": "parseTimestamp(uint256)", "0xd3c0715b": "vote(uint256,bool,string)", "0x6b069710": "scheduleCall(address,bytes,uint256,uint256,uint8)", "0x37ae43a3": "BetOnHashV81()", "0xab519020": "calcShare(uint256,uint256)", "0x6572ae13": "calculateWinner(uint256,uint256)", "0x6aaba012": "ErrorGenerator()", "0xfe05e8b1": "assertFact(uint256,string)", "0x6e940a29": "changeHost(address)", "0x669ee827": "RegisterDevice()", "0x6f4dd69c": "testSetBalanceUpdatesSupply()", "0x4401a6e4": "safeSend(address)", "0x27dc297e": "__callback(bytes32,string)", "0xe4dedc7f": "DeleteContract()", "0x7fef036e": "totalEntries()", "0x64325ddb": "currentClaimPrice()", "0x2fc0aad3": "isNumericString(string)", "0xbc45d789": "setConfigUint(int256,bytes32,uint256)", "0xee95feaf": "isSeller(address)", "0x358d5dc2": "getIsCashed(uint256,uint256)", "0x1397fdbd": "getShares(address,bytes,int256[])", "0x2d8c1c35": "level_up()", "0x24600fc3": "withdrawFunds()", "0x05f8b6f5": "_rewireIdentities(bytes32[],uint256,uint256,uint32)", "0x1840f0ca": "countVotes(uint256)", "0xd44aadf7": "initROS()", "0xca1d209d": "fund(uint256)", "0x5fa513d5": "findPtr(uint256,uint256,uint256,uint256)", "0x3c314a91": "playerGetPendingTxByAddress(address)", "0xd5582205": "getCertifiedStudentAtIndex(uint256)", "0xe45ebe93": "checkVoteStatus()", "0xcd9380d5": "testSetBalanceSetsSupplyCumulatively()", "0x637e86eb": "totBOTs()", "0x5bb47808": "setFactory(address)", "0x674cc1f5": "getMarketHashes(bytes32[])", "0x648bf774": "recover(address,address)", "0x0221038a": "payOut(address,uint256)", "0x4016535a": "parseBlock(bytes,uint256)", "0xa3908e1b": "convert(uint256)", "0xd9e7ee1c": "new_game(uint256,uint256)", "0x929e626e": "getShareDistribution(bytes32)", "0xa20495d3": "Managed()", "0xd409ddda": "EtherizationUtils()", "0xcb2b9031": "addressToBytes(address,address)", "0xfff3c457": "readMessages(uint256)", "0x043753ba": "makeDecision(uint256,bool)", "0x85b4bb53": "getSettings()", "0x60726abb": "copy()", "0xe50d0473": "SetRank(uint8,address,uint16)", "0x54ae8492": "CustodialForward()", "0xd6d02c51": "whois(string)", "0xcb712535": "_transferFrom(address,address,uint256)", "0xb152f19e": "getFirstSchedulableBlock()", "0x9334ab61": "Infos()", "0x88a49164": "testErrorUnauthorizedTransfer()", "0x17db59a4": "dEthereumlotteryNet(address,address,address)", "0xf85aefba": "testBitsSetFailIndexOOB()", "0xae99847b": "daylimit(uint256)", "0xd93e7573": "disown(bytes32)", "0xa5468081": "Pyramid(address)", "0x00e7d289": "registerListening(address)", "0x57ee24af": "getNum(bytes32,uint256)", "0xdaea85c5": "approve(address)", "0x36ffa905": "getMyProposals()", "0x7143059f": "getParticipant(address)", "0x55ff440a": "castStringToUInt(string)", "0x6a4a6b6e": "_myAddressHelper()", "0xb67fabdf": "scheduleTransaction(address,uint256,uint256,bytes)", "0xbcca1fd3": "changeVotingRules(uint256,uint256,int256)", "0x1d3390a1": "carefulSendWithFixedGas(address,uint256,uint256)", "0x45104b16": "EXECUTION_GAS_OVERHEAD()", "0xa26759cb": "addFunds()", "0x232523e8": "forceDivestOfAllInvestors()", "0x7e904a48": "getNumContents(uint256)", "0xb69c0896": "BaseScheduler(address,address,uint256)", "0xc6ed8e1b": "getApprovedProxys()", "0x4d1f8c31": "owner(uint64)", "0x17c65aa7": "getMaxLossAfterTrade(address,uint256,int256,int256)", "0x2c02d622": "precalculate()", "0xa035b1fe": "price()", "0x43b0e8df": "set(uint256,uint256,uint256)", "0x9b5fde7d": "payOut(uint256,string)", "0x89fcd099": "getApproval(address,address)", "0x4c0eceb5": "plusOnePonzi()", "0x880cdc31": "updateOwner(address)", "0xdab80d6f": "addSponsor(address)", "0x0fcda174": "getAccountTokenBalance(address,address)", "0xa55cab95": "getName(uint8,uint8)", "0x934db458": "Big()", "0xeb782d8c": "ContentSeries(address)", "0xdbfef710": "getDefaultRequiredGas()", "0x4f09eba7": "proxyApprove(address,uint256,bytes32)", "0xf4c5ab7c": "validateCallGas(uint256,uint256)", "0x376fe102": "userId(address)", "0x922dd59a": "icapTransfer(bytes,address,bytes,uint256)", "0x7318b453": "setVotetUntil(uint8)", "0xb8c86aa6": "getArraySettingResult()", "0x37bdc99b": "release(uint256)", "0x7cbcc254": "__reset__()", "0x37664643": "retractLatestRevision(bytes32)", "0x4b031d0f": "shortSellShares(bytes,uint8,uint256,uint256)", "0xad8d5f48": "exec(address,bytes,uint256)", "0x2f95b833": "requiredStackDepth()", "0xe3848e5b": "thing(string,string,string)", "0xaa272d4b": "getNodeIndexId(bytes)", "0xd7f746ce": "tickingBomb()", "0x3b84edbd": "setRNG(address)", "0x1fb2f2a0": "testUpdateLatestRevision()", "0xb7fba4d3": "getProxy(address)", "0x4b8e1ba8": "isMinter(int256,address)", "0xba4c206e": "removeCertificationDocumentInternal(address,bytes32)", "0x884b5dc2": "fill(uint256[])", "0x88017e05": "setContribution(uint256)", "0x1ff517ff": "totalDebt(address)", "0xd0315658": "getShareDistributionWithTimestamp(bytes)", "0x7d03f5f3": "newGame()", "0xb7538f3e": "ChangeClient(address)", "0xbf4d89b5": "parseInt(string,uint256)", "0x7b55c8b5": "scheduleCall(address,bytes4,bytes,uint8,uint256[4])", "0x350d141e": "getWasApprovedBeforeDeadline()", "0x27960c5f": "validateEndowment(uint256,uint256,uint256,uint256,uint256,uint256,uint256)", "0xb774d3d7": "BankOwner_GetDonationsBalance()", "0x267b6922": "entries(bytes32)", "0x08b7c13b": "getExists(bytes20)", "0x7e7a2fbf": "contribute_toTheGame()", "0x5b86914d": "bet_value()", "0x0e1087c3": "getMarketMakerFunds()", "0xf7149220": "RNG()", "0x345006b6": "getGenerationForCall(address)", "0xc4b14e0b": "getSignature(bytes32)", "0x419945f8": "ExpiringMarket(uint256)", "0x41868769": "CallAborted(address,bytes)", "0x29092d0e": "remove(address)", "0x746c9171": "m_required()", "0x5020dcf4": "convertToEach(uint256,string,uint256)", "0xa06db7dc": "gracePeriod()", "0xbf8fc670": "sendToAggregation(uint256)", "0xf14fcbc8": "commit(bytes32)", "0xa538d287": "getMinMax()", "0xcae523c1": "testOwnedTryAuthUnauthorized()", "0x04d10f1c": "isValidChainyJson(string)", "0x9ba5b4e9": "getEventHashes(bytes32[])", "0xfedfd535": "Config()", "0x42ce0f30": "testThrowUpdateLatestRevisionNotOwner()", "0x31be6985": "testBitXorSuccess()", "0x173cb7de": "getNumReleasesForNameHash(bytes32)", "0xd90a88cd": "getContentReplies(uint256,uint256)", "0x92eefe9b": "setController(address)", "0x052b81c7": "releaseBadges()", "0xb2855b4f": "setFeeAddr(address)", "0x19a9c2f1": "generateId(string)", "0xfa9acb05": "addressInArray(address,address)", "0x3da5c3ce": "puzzle(address,bytes32)", "0x7a427d98": "forceReturn()", "0x70e71ea3": "etherandomSeedWithGasLimit(uint256)", "0xd7a58658": "changeHouseedge(uint8)", "0x72b75585": "getOriginalClient()", "0xf802075f": "requiredEndowment()", "0x7997b997": "doMelt(uint256,uint256)", "0x6d5433e6": "max(uint256,uint256)", "0xb651cbaf": "add_level(address,bytes)", "0xb4d6d4c7": "getPackageData(bytes32)", "0x90e3c278": "getShares(uint256[128])", "0x179b73da": "killBoardProposal(uint256,address)", "0xc944a38e": "CharlyLifeLog(string,int256)", "0xe1c66292": "Create(uint32,address)", "0x69c8b344": "ownedToken(address)", "0xabcf1328": "InterestBank()", "0x532e7e6a": "calcEarningsSelling(bytes,uint256,uint256[],uint8,uint256)", "0x43d24a5e": "addUpdater(address)", "0xd1feca67": "addSpendingRequest(address)", "0x2d34ba79": "setup(address,address)", "0xcb14d93b": "getHash(bytes,address,uint256)", "0x309424fe": "get_all_names()", "0x96c52fc3": "____forward(address,uint256,uint256,bytes)", "0xde39acea": "get32(bytes,uint256)", "0xf3dd3d8a": "newCurrency(string,string,uint8)", "0x2432eb23": "testThrowRetractLatestRevisionNotUpdatable()", "0x7fcf3a2f": "throwFooBar()", "0xabe9f569": "oraclize_getPrice(string,uint256)", "0x41ee903e": "clear(uint256,uint256)", "0xd249a52e": "update(bytes,uint256[],uint256[])", "0xc3d014d6": "setContent(bytes32,bytes32)", "0x3ac5cb73": "GeometricPonzi()", "0x4a1aa767": "claim_victory(uint256,uint8,uint8,uint8)", "0xce592586": "setThresold(uint256,uint256)", "0x63deb2c5": "changeMemberAddress(address)", "0x2e6e504a": "trusteeWithdraw()", "0xcfed9199": "timePassed(uint256)", "0xb782fc9b": "getFirstActiveDuel2()", "0x35b28153": "addAuthorization(address)", "0x46f7a883": "BuyTicket(uint8,uint8,uint8)", "0x83c51a38": "thesimplegame()", "0xfa28ba0d": "validateReleaseLockfileURI(string)", "0xa7b2d4cb": "remove(int256,address)", "0x010731c0": "sendCryptedHand(bytes32)", "0xe9e99d81": "getChannelFeed(address,uint256,uint256,uint256)", "0x4e30a66c": "safeToAdd(uint256,uint256)", "0x2c4e591b": "totalGames()", "0xa3221c8e": "step8()", "0x783ce458": "expmod(uint256,uint256,uint256)", "0xe417291b": "undelegateDAOTokens(uint256)", "0x8e5d97a2": "releasePendingTransfer(uint256)", "0xbc5ff5e1": "oraclize_query(string,string[4],uint256)", "0x38f77d69": "getDistributeProfitsInfo()", "0xbb510a77": "createChannel(address,uint256)", "0x650955d4": "HashToken()", "0xa8484938": "doApprove(address,uint256)", "0x64ed31fe": "authVotes(address)", "0xf7ae9421": "checkInvestorBalance(address)", "0xba904eed": "removeRegistrar(address)", "0xdce4a447": "at(address)", "0xdb4cacec": "Other()", "0x3647b87a": "buildFactory()", "0xa51aea2d": "changeMaxMultiplier(uint256)", "0x4974bc27": "download()", "0xf8a8fd6d": "test()", "0xd8c90762": "addTrustedIssuer(address,string)", "0xdf6c13c3": "getMinFunding()", "0x867904b4": "issue(address,uint256)", "0x1531c267": "fipsRegisterMulti(uint256,address,bytes)", "0x40a49a96": "searchSmallestInvestor()", "0x61bffe01": "addIdentities(bytes32[],bytes32[])", "0xf77a0923": "BitcoinProcessor(address)", "0xd02528e6": "GetGameIndexesToProcess()", "0x9f927be7": "getNextCall(uint256)", "0xd8162db7": "lockedUntilBlock()", "0x36dfe260": "payOneTimeReward()", "0xc5b1a53c": "deposit(bytes16[],uint64)", "0xc2b6b58c": "isClosed()", "0xc88cc6ac": "getCertification(address)", "0x77ac3da5": "oraclize_query(uint256,string,string[1],uint256)", "0x70ab8ba8": "creditUpdate()", "0xd3ea3322": "testBuildTokenSystemCost()", "0x72388610": "paybackAll()", "0xca6d56dc": "addMember(address)", "0x0994a0a0": "DSTokenTest()", "0xe53e04a5": "refillGas()", "0xc1d5e84f": "addNewUser(address)", "0x89ed0b30": "setOraclizeGas(uint32)", "0x02ba8742": "sendCoins(address,uint256)", "0xb0de1cb7": "publish(uint64,bytes,uint64)", "0x0e13b9af": "getValue(uint8,uint8)", "0xb3dfcdc3": "Contribution(uint256)", "0xa9b35240": "packageExists(bytes32)", "0xd1d3bb92": "testSetPkg()", "0x97297467": "checkAndVerify(bytes)", "0xe31bfa00": "next_id()", "0x9948e493": "calcMarketFee(bytes,uint256)", "0xd148288f": "setHoldingPeriod(uint256)", "0xc032dc30": "execute(uint256,address)", "0xdad99989": "burnCoins(address)", "0xb1999937": "leapYearsBefore(uint256)", "0xa6cbcdd5": "numSignatures(bytes4)", "0xaca66aec": "DVIP()", "0x20bf0c52": "Derived(uint256)", "0x693ec85e": "get(string)", "0x0411bca8": "getChallengeAnswerResult(uint256)", "0x61584936": "sealedBids(bytes32)", "0x2f1927cb": "prepareRoll(uint256,uint256,uint256)", "0xeaa1f9fe": "reqisterListening(address)", "0xb623f5e5": "checkSetCosignerAddress(address)", "0xa88c5ef7": "NextPayout()", "0x66ad484c": "newfirst_player(address)", "0xb4022950": "collectFeesInEther(uint256)", "0xbff0fbb8": "calculateMeat(uint256)", "0xd62b255b": "setOwner(address,string)", "0x2fd6d40b": "getBetValueByGamble(uint8)", "0x3b0506f7": "getVoteByAddress(address,uint256)", "0xbddd3a6b": "step7()", "0x67fbd289": "destroyTokens(uint256)", "0x9844347b": "createCertificate(bytes,bytes,uint256,bytes)", "0x5e68ac2c": "Kingdom(string,address,address,address,uint256,uint256,uint256,uint256,uint256)", "0x8ba9f354": "testClearBitSuccess()", "0x48027610": "transferPaidOut(address,address,uint256)", "0x912de8de": "fixBalance()", "0x04509918": "scheduleCall(address)", "0x7cf0ffcb": "forceDivestAll()", "0x3b3b57de": "addr(bytes32)", "0xeb7c6f72": "step6()", "0xfe6f0d82": "testConstructorEvent()", "0x55b62dcf": "getThresold(uint256)", "0xfbae5e7d": "Investors(uint256)", "0x29e206bd": "forceDivestAll(bool)", "0x6a226a49": "addMessage(string)", "0x8e2a6470": "allocateShares(address,uint256)", "0xe6e7237f": "claim_time_victory(uint256)", "0x17a601b5": "MAX_STACK_DEPTH_REQUIREMENT()", "0x87fd0421": "TheEthereumLottery()", "0xc17e6817": "sendSafe(address,uint256)", "0xa5dfee67": "testThrowsCreateNewRevisionNotUpdatable()", "0xb35893f3": "setExporter()", "0x1ceea715": "GetMyInvestFee()", "0xb78bd4a5": "breakCookie(string)", "0x05215b2f": "createStandardToken(uint256)", "0x2632bf20": "unblockMe()", "0x5292af1f": "sendBalance(address)", "0xc2e9fab3": "SubUser()", "0x6493d7fc": "CircuitBreaker(address,address,uint256,uint256)", "0x4f896d4f": "resolve(uint256)", "0x16870257": "getTileDescription(uint8,uint8)", "0x3ef87414": "getRevisionCount(bytes20)", "0x747586b8": "setInt(int256)", "0x5714f6a1": "getTotalAvailableRelays()", "0x99154b49": "ARK()", "0x1efb17ee": "changeHouseAddress(address)", "0x354d7e40": "Payout()", "0x2da0d1ea": "etherSold()", "0xea46193e": "getEtherBalance()", "0x11fe773d": "memcpy(uint256,uint256,uint256)", "0x1e701780": "MICRODAO(address,uint256,uint256,uint256,address)", "0x1c31f710": "setBeneficiary(address)", "0x0a4caed0": "getChannelByRank(address,uint256)", "0xf3125a1f": "deposit(address,uint256,bytes,uint256)", "0x00e46700": "setMinimumPassPercentage(uint8)", "0x92d282c1": "Send()", "0x89d59ee5": "createPersonalDepositAddress()", "0xbe1c766b": "getLength()", "0x70a08231": "balanceOf(address)", "0xae0a6b28": "signature(string,bytes32)", "0xb3485dca": "UpdatePrice(uint8,uint32)", "0xf8ec4bf2": "setAllowTransactions(bool)", "0x53d97e65": "setPrizes(uint32[])", "0xd1b1a22b": "set(string,uint256[],uint256[],uint256[],bool[],uint256[])", "0x96286cc9": "isTokenOwner(address)", "0x154af6b1": "sendShares(uint256,uint8,uint256,address)", "0xbe2430fe": "sendValues()", "0x57a373a1": "uintInArray(uint256,uint256,int256,uint256[],uint256)", "0x8fd28bcf": "testFailAuthorityAuth()", "0x89ef40e7": "numberOfHealthyGenerations()", "0x23e9c216": "setBounty(address,string,uint256)", "0x71dd99fe": "BigRisk()", "0x1e9fcc77": "activateAllowance(address,address)", "0x561e91a1": "makeBet()", "0x32d2fb9f": "getRefRemainingTime(uint256)", "0x992c870d": "transferName(bytes,address)", "0x6b3fdc5a": "oraclize_setNetwork(uint8)", "0x2ea459b8": "claimThrone(bytes)", "0x33a99e04": "selectWinner()", "0x3b49a77b": "hasConfirmed(bytes,address)", "0xa352f1a8": "calcSHA3(bytes)", "0x4bb4b260": "cashAllOut()", "0xb89a73cb": "isShareholder(address)", "0xba5a2d33": "exitPool(address)", "0xddd41ef6": "transferDirector(address)", "0xa06cab79": "Registrar(address,bytes32)", "0x871113c3": "oraclize_query(string,string[1],uint256)", "0x1f0f711f": "discontinue()", "0x632f0ba6": "descriptionHashes(bytes)", "0x980e8c81": "FutureBlockCall(address,uint256,uint8,address,bytes,uint256,uint256,uint256)", "0x4ae8c55f": "getWwLength()", "0x82fc49b8": "setCosignerAddress(address)", "0xc4bd8ebc": "num_monsters()", "0x0381cb3b": "setRowcol(uint256,uint256[2])", "0x124eaee6": "Identity()", "0x3f4be889": "callContractAddress()", "0xef3a6031": "testBaseToken()", "0x954ab4b2": "say()", "0x1b855044": "getHash(uint256,uint256)", "0xd9d73887": "Diana()", "0x5103a5a3": "certify(address,bytes32)", "0x51560da9": "topDogInfo()", "0xf3ee6305": "removeCertificationDocument(address,bytes32)", "0x049ae734": "scheduleCall(address,bytes4,uint256,uint256,uint8)", "0xd8a8e03a": "move(uint256,address)", "0xc3c5a547": "isRegistered(address)", "0x06005754": "nameRegAddress()", "0xbe592488": "validateName(bytes)", "0x0eecae21": "draw()", "0xac3e7d24": "addChainyData(string)", "0xfd83f3e3": "QueueUserMayBeDeliveryDroneCotnrol()", "0x7772a380": "isInGeneration(address,uint256)", "0xeb1ff845": "changeId(uint256,uint256,uint256)", "0x9cc9299e": "killSwap()", "0x1e2ca0f3": "updateLeftLottery(address)", "0x998446a8": "acceptRequest(uint256,bytes)", "0x8e1ffb19": "testThrowsRetractLatestRevisionEnforceRevisions()", "0x9935935f": "setResolveHandler(bytes,address)", "0xcd4b6914": "getRandom(uint256)", "0xc08dd1dc": "IOU(string,string,uint8)", "0xbe4054b9": "commitReading(address,uint256,uint256,string)", "0xbc21ce9d": "Aggregation()", "0x6e173a7f": "storeBlockHeader(bytes,bytes)", "0x114d69b2": "setCRLaddr(address)", "0x3fa4f245": "value()", "0x69573648": "remove(bytes,bytes)", "0x7fee4ecb": "GAS_PER_DEPTH()", "0x591c515f": "append(string,string)", "0x727b1cd6": "next_draw(bytes32,uint256,uint256,uint256,uint256,uint256)", "0xc60ce271": "findNextMinute(uint256,bytes)", "0xd337616e": "resetLottery()", "0xdacaeb07": "pledge(bool,uint256)", "0xb29c2493": "token(uint256,string,uint8,string)", "0x61047ff4": "fibonacci(uint256)", "0x8f367001": "numTokensAbleToPurchase()", "0x12cc08f2": "getPackageReleaseHashes(string,uint256,uint256)", "0x67a59d91": "scheduleCall(address,bytes,bytes,uint256,uint256,uint8)", "0xe6c3b4ab": "testBalanceAuth()", "0xd526b9bd": "_allow()", "0x29de91db": "setMsg(address,uint256)", "0xd1cf113e": "multiAccessSetRecipient(address)", "0xc2412676": "Token()", "0x391f2e96": "InvestCancel()", "0xc0ae6a3a": "ultimateOutcomes(bytes)", "0x202d6eaf": "addInvestorsValue(uint256)", "0x30b9af98": "withdrawFunding()", "0xe80bd3e5": "addCertificationDocumentToSelf(bytes32)", "0xf4e36afd": "findThroneByNameHash(uint256)", "0x30677b83": "multiplierFactor()", "0x590528a9": "sellShares(uint256,uint8,uint256,uint256)", "0x01cceb38": "setExpiry(uint256)", "0x779beca0": "getNumOfSalesWithSameId(bytes)", "0xac940823": "betOnLowHigh(bool)", "0x06961560": "DAO(address,uint256,uint256,uint256,address)", "0xd42bf301": "doTriggerTryAuth()", "0xfa566ddd": "doAllowance(address,address)", "0x6677febe": "get_all_accepted()", "0xaa67c919": "depositFor(address)", "0xf28386bf": "Nexium()", "0x77e4fb04": "testFailNotEnoughValue()", "0x12b58349": "getTotalBalance()", "0xc0d2834b": "DataSource()", "0x3e82055a": "addSignature(uint256,bytes16,bytes)", "0xcff2fa42": "_returnFee(address,uint256)", "0xa056469a": "extractFeeLength()", "0xc98031be": "hintURL(int256,bytes32,string)", "0x6ebbe863": "updatePublishContract(address)", "0x08216c0f": "createHumanStandardToken(uint256,string,uint8,string)", "0xc36af460": "getLatest()", "0xdb5b4183": "oracleOutcomes(bytes,address)", "0x0b5ab3d5": "destroyDeed()", "0xe1c7392a": "init()", "0x4ca1fad8": "addRequest(uint256)", "0x305b73d9": "configure(address,address,uint256,uint8,bytes32,bytes32)", "0x9077dcfd": "submitCoding(string,uint256)", "0x38fff2d0": "getPoolId()", "0x07bc6fad": "withdraw(address,uint256,bytes32,uint256)", "0xfbf58b3e": "transfer(string,address)", "0x1d8b70da": "order_received(string)", "0x0b3ed536": "claimDonations(uint256)", "0x6f374a12": "setBool()", "0x0ca35682": "recover(uint256)", "0x3ae7cdfa": "fipsLegacyRegister(bytes20[],address)", "0xe6c1beb4": "prepend(address)", "0x776d62f6": "costs()", "0xe4690a0b": "popRequest()", "0x74eb9b68": "isAccountLocked(address)", "0x7d32e7bd": "transfer(address,bytes32)", "0xdf2f0a4a": "getDecisionBlockNumber(uint256,uint256)", "0xc494f71a": "LedgerFund(uint32,uint32,uint64,uint64)", "0x446d5aa4": "getAttributes(address)", "0x4cdc6a73": "Marriage()", "0x677cee54": "SafeConditionalHFTransfer()", "0x7b48ba20": "testThrowDisownNotOwner()", "0x1288c42a": "Prism()", "0xe8b13c44": "getChainyTimestamp(string)", "0xe4c2db06": "getPreviousFile(bytes)", "0xf0586f0d": "doThrow(bool)", "0xc1b06513": "registerEvent(bytes32[])", "0x521eb273": "wallet()", "0x32254992": "getPrevHash(int256)", "0x1fd96b69": "ManagedAccount(address,bool)", "0xabf74a93": "pitFee()", "0xa480ca79": "collectFees(address)", "0xa0bde7e8": "getShareDistributionWithTimestamp(bytes32)", "0xff27c476": "shiftBitsRight(bytes,uint256)", "0x172d8a30": "setDirectorLock(uint256,uint256)", "0xf262de8c": "add_staircase(uint16)", "0x990f3f53": "computeResponseSecondHalf(uint256,uint16)", "0x26745909": "PRNG_Challenge()", "0xcacc24eb": "transferFromViaProxy(address,address,address,uint256)", "0x94f3f81d": "removeAuthorization(address)", "0x3f0ec70b": "RequestFactory(address)", "0xa2a8336f": "claimEtherSigner(uint256)", "0xaa5d4719": "getTransferable(bytes20)", "0x23cd7cd5": "Model()", "0x3fb0b2c9": "CancelRoundAndRefundAll()", "0xd5fa2b00": "setAddr(bytes32,address)", "0xa0f61310": "FakeRelay(bytes)", "0x4ea66c38": "buyinInternal(address,uint256)", "0xbe040fb0": "redeem()", "0xb845c9a2": "WEI()", "0x26a7985a": "getMaximumCallGas()", "0x06661abd": "count()", "0xc89f8f08": "testGetController()", "0x81baf820": "BlockScheduler(address)", "0x9801cb8e": "ProofOfExistence()", "0xeb7492d1": "testTotalSupply()", "0x3dfb4843": "renewDeed(bytes32)", "0xc3fa5f93": "BlockScheduler(address,address)", "0x7958533a": "meta(uint256,bytes32)", "0xa1a66e56": "deductFunds(uint256)", "0xaf92a693": "addRegistrar(address)", "0xb2aac51f": "lookupUser(string)", "0xd70cf105": "moveBalance(address,address,uint256)", "0x2afb21bc": "InvestWithdraw()", "0x6d09e2ec": "commitCurrency(address,uint256,uint256)", "0x7b1a4909": "transferETH(address,uint256)", "0x96c824a8": "createAccountFundContract()", "0xe0a70811": "restart(bytes20,bytes)", "0x22057bc7": "getAllRevisionBlockNumbers(bytes20)", "0x6af2da2f": "testKeyedHash()", "0x7f6d8955": "RegisterOne(uint32,address,address)", "0x65f27bea": "testSubBalanceFailsBelowZero()", "0xa2f16d80": "dexWithdrawCollectedFees()", "0xc179520c": "ManageAccount()", "0x2672b3e2": "SplitterEtcToEth()", "0xe839e65e": "query2(string,string,string)", "0x39f64b52": "calcTokenPrice()", "0x4ef5710a": "WatchNumberOfPlayerInCurrentRound()", "0x3017fe24": "callAPIVersion()", "0x2977b1b1": "testAllowanceStartsAtZero()", "0x531c1b33": "getOperatingBudget()", "0xb7f2f33c": "transferRightIfApproved(address,bytes)", "0x00873367": "comparisonchr(string)", "0x2a0d79ef": "totalSupply(bytes)", "0xa715ff59": "EtherandomProxy()", "0xd6ca8ccb": "disown(bytes20)", "0x6ad2a0b3": "buildContract(address)", "0x45596e2e": "setFeeRate(uint256)", "0x0e97cfdf": "placeOrder(uint256,uint256,uint256)", "0x9549355e": "oracalizeReading(uint256)", "0x8d7af473": "numberOfProposals()", "0x728af7ec": "getInterest(uint256,uint256)", "0x11b9fee8": "ForkChecker(uint256,bytes32)", "0xd850288b": "etherlist_top()", "0xf4dc2d21": "Deed(uint256)", "0xf8b11853": "getGenerationStartAt(uint256)", "0x7c7a52bf": "newChallenge(uint256,address)", "0xd2d4bd72": "getCrossRate(bytes,bytes)", "0xe9b93569": "OwnerGetFee()", "0xfb72d24e": "shift_right(uint64,uint256)", "0x112e39a8": "scheduleCall(uint256)", "0x6c494843": "multiAccessChangeOwnerD(address,address,address)", "0x313ce567": "decimals()", "0x9bac8602": "testFailAddBalanceAboveOverflow()", "0xa70a9ad7": "switchDeity(address)", "0x6a61e5fc": "setTokenPrice(uint256)", "0x990c8f79": "returnValue()", "0xa4136862": "setGreeting(string)", "0x0af4626d": "testRetract()", "0x5e11544b": "newPeriod()", "0xdc206e5f": "oraclize_query(uint256,string,string[])", "0xcaa648b4": "getTotalValue()", "0x20bfec70": "WatchFees()", "0x62a0b56b": "testUnset()", "0x42f6e389": "isModule(address)", "0x769796fe": "resetAction(uint256)", "0x402e6230": "getTotalGambles()", "0xe8a1c08f": "nibbleToChar(uint256)", "0x1aa3a008": "register()", "0x96d02099": "rsplit()", "0x83324e8c": "numGroups()", "0x72c7c85a": "minority()", "0xb8d94b95": "buildDSNullMap()", "0xe039e4a1": "getOwner(uint8,uint8)", "0x625cc465": "baseDonation()", "0x77372213": "setName(bytes32,string)", "0xa7dfc874": "unregister(bytes,address,uint256,bytes)", "0x37f4c00e": "anchorGasPrice()", "0xb2bfd948": "checkNumbers(uint8[3])", "0x512f1e64": "orderBookLength()", "0xafed762b": "toSlice(string)", "0xbb6a1427": "testThrowRestartEnforceRevisions()", "0x734d8287": "unclaimedFees()", "0xf295206f": "_unsafeSend(address,uint256)", "0x69d01268": "concatUInt(uint256)", "0x0494630f": "oraclize_query(uint256,string,string[4],uint256)", "0x13fc6ac2": "getEventData(bytes32)", "0xbff974e8": "getContentReplies(uint256)", "0x18921de4": "addSignature(string,uint256[],uint256[],uint256[],bool[],uint256[])", "0xa87e7552": "isValid(bytes,bytes)", "0xb8d3bfe3": "MeatGrindersAssociation(address,address,uint256,uint256,uint256,address)", "0x61461954": "execute()", "0xecb0256b": "relayTx(bytes,int256,int256[],int256,int256,bytes,int256,int256[],int256,int256)", "0x7cdbae63": "addRegistryIntoTagsIndex(address)", "0x1f4e996b": "challenge(bool)", "0x0eb0afa6": "createDebt(address,address,uint256)", "0x5f6a1301": "clearPending()", "0x305a762a": "getTicketsCountByBuyer(uint256,address)", "0x724ae9d0": "getMinInvestment()", "0x1e39499d": "scheduleCall(address,bytes,uint256)", "0x4f197ee7": "transferPackageOwner(string,address)", "0x7e3faec1": "GoldTxFeePool(address,address,bytes)", "0x8d68cf59": "sendFunds()", "0x83eed3d5": "queryN(uint256,string,bytes)", "0x15c91115": "pbkdf2(bytes,bytes,uint256)", "0xeb121e2f": "update(uint256,uint256[101][])", "0x5e44daf3": "vote(uint256,int256)", "0xac562666": "freezeCoin()", "0xb0166b04": "testTransferringMkr()", "0x631de4d6": "replace(address,address)", "0x4bd70ea3": "testFailGetUnset()", "0xf738e5ca": "ownerTakeProfit()", "0xc6236a5c": "scheduleCall(bytes,uint256,uint256,uint8,uint256)", "0x119aa5c8": "checkForward(bytes)", "0x541aea0f": "put(uint256,uint256)", "0x6386c1c7": "getUserInfo(address)", "0x4e209678": "testFailBreach()", "0xe9fe799e": "registrantRemove(address)", "0x2aee19c7": "testCreateWithNonce()", "0xa0ec4e09": "getUltimateOutcomes(bytes32[])", "0x4d9e4e22": "Etheria()", "0xa6b513ee": "finalPrice()", "0x82f0d875": "makeHash()", "0x78ae88d1": "newDeal(uint256,uint256,uint256,uint256,uint256)", "0x177766e6": "getOptionChain(uint256)", "0xf1173928": "RemovedFromGeneration(address,uint256)", "0xea2ea847": "testChallengeFinalize()", "0xbd35d570": "GAS_TO_COMPLETE_EXECUTION()", "0x364ea9e7": "set(uint256,uint256,bool[],uint256[])", "0x17ff0caa": "WeatherBet(uint256,address,address,address)", "0x4e23a144": "fundUser(address,uint256)", "0x144267e0": "refundSecurity(address,uint256,uint256)", "0x31c6c4cf": "transferFromWithReference(address,address,uint256,bytes32,string)", "0x2eb5c61f": "testThrowsUpdateLatestRevisionEnforceRevisions()", "0xde640e19": "Investment(uint256)", "0x9cb8a26a": "selfDestruct()", "0x9c43d950": "registration(uint256,uint256,uint256)", "0xe2fdcc17": "escrow()", "0xc618d15f": "ConvertNumbers(bytes5)", "0x8c98117c": "getBill(uint256,uint256)", "0x2d7788db": "rejectRequest(uint256)", "0xfaab9d39": "setRegistrar(address)", "0xa289673b": "fipsChangeOwner(bytes20,address,address)", "0x54d9d6f8": "findNextDay(uint256,bytes)", "0x008a745d": "claimDividendShare(uint256)", "0x6f4812e2": "testFailControllerInsufficientFundsTransfer()", "0x5e983d08": "setPrices()", "0x798974dd": "getNumProposals()", "0x4ca168cf": "register(bytes,uint256,address,string,uint256)", "0xa1e4d3c2": "MembershipRoster()", "0xd4065763": "returnRemainingMoney()", "0x0c4326a0": "getMajorMinorPatch(bytes32)", "0xeece1e1f": "scheduleShuffling()", "0x226685ee": "Visit()", "0x323082d7": "Vote(string)", "0x0b15650b": "randInt(uint256,uint256)", "0xc9cfac55": "refundCurrency(address,uint256,uint256)", "0xfe4a3ac9": "setExecPrice(uint256)", "0x6a0e605f": "MyToken(uint256,string,uint8,string,address)", "0xb549793d": "scheduleCall(bytes4,bytes,uint256,uint256,uint8,uint256)", "0x85e68531": "revokeAccess(address)", "0x01991313": "scheduleCall(address,bytes4,uint256)", "0x0a6be0e7": "BalancedPonzi()", "0xf463edd1": "createDocument(uint256)", "0xa20c404f": "ModifySettings(uint256,uint256,uint256,uint256,uint256,uint256,uint256)", "0x560bb612": "SignatureValidator(address)", "0xf7654176": "split()", "0x48f05187": "scheduleCall(address,bytes4,bytes,uint256)", "0xf2b904c3": "checkBetColumn(uint8,address,bytes32,bytes32)", "0x7bc0ff20": "setupExportFee(address,uint256)", "0xeb06e65e": "allowanceFromProxy(address,address,address)", "0xfe757fb5": "lastClaimPrice()", "0xa5d0bab1": "buyPartial(uint256,uint256)", "0xda7d0082": "isCertification(address,bytes32)", "0xe570be18": "DVIPBackend(address,address)", "0x54738157": "OwnerCloseContract()", "0xc1e5304a": "CreateNewDraw(uint256,bytes)", "0x0c26e42e": "getReleaseHashForNameHash(bytes32,uint256)", "0x0f7d6673": "Channel()", "0x54ea4000": "identify(address[])", "0x69307c80": "rotateBits(bytes,int256)", "0x78f0161a": "setGreyGreenPrice(uint8)", "0x23b872dd": "transferFrom(address,address,uint256)", "0x578bcc20": "reduceDebt(address,address,uint256)", "0x59e148fc": "getLastOfferId()", "0xb5299ca6": "giveMeat()", "0xae30d35d": "ARK_TROGLOg_1_00()", "0x2d2c44f2": "Vault()", "0xce19419b": "testThrowsSetNotUpdatableNotOwner()", "0xffcf21a9": "eventOracles(bytes,uint256)", "0xf46c50dc": "doFail()", "0x73b55eaf": "registerData(address,int256,bytes32,address)", "0x53770f9a": "isStateless()", "0x4d47feaa": "ShareholderDB(uint256)", "0x40b31937": "pledgeDecline(uint256)", "0x01cb3b20": "checkGoalReached()", "0x62e05175": "setMotionDB(address)", "0xf362d78f": "testBitNotEqualSuccess()", "0xd2531590": "CANCEL_EXTRA_GAS()", "0x9a92b7e7": "EthVenturesFinal()", "0x79b0797c": "AmIPlayer1()", "0x6241bfd1": "Token(uint256)", "0x94a1710d": "testNonOwnerCantBreach()", "0xb466b76f": "fresh()", "0x4a5dddd2": "proxyPurchase(address)", "0xc0a1a949": "x15()", "0xc3b8bfe5": "transferIfNoHF(address)", "0x4a7e049e": "getFullCompany(address,uint256)", "0x481b659d": "permitPermanentApproval(address)", "0x16ce8a69": "setBuilding(uint256,uint256)", "0x1593a8c7": "endLottery()", "0x078c3fa4": "_transferToICAPWithReference(bytes32,uint256,string)", "0xfa3f1e99": "testBlobStoreRegistered()", "0x0b9e9817": "CanaryV7FastTestnet()", "0x6663bbec": "orderMatch(uint256,uint256,int256,uint256,uint256,address,uint8,bytes,bytes,int256)", "0x273bc3c9": "numberOfThrones()", "0x3c84f868": "set(int256,address,uint256)", "0x1ac61e8c": "testBlobCreate()", "0x5ccc3eaa": "roundMoneyUpToWholeFinney(uint256)", "0x0ccec396": "getNumReleases()", "0x6ac6205c": "addDataPoint(int256,uint256,bool,string)", "0x1d124fe4": "setUtils2(address)", "0x4c471cde": "scheduleCall(address,bytes4,bytes,uint256,uint256,uint8,uint256)", "0x52a554a1": "voteBoardProposal(uint256,address,bool)", "0x745a8be2": "flip32(bytes)", "0xbac1e2e0": "testBitsAndSuccess()", "0x25fda176": "notify(address,uint256)", "0x3b8e6f2e": "balanceAt(address,uint256)", "0x60585358": "getByte()", "0xc853c03d": "newDraw(uint256,uint8[3],uint256,uint256,uint256,uint256)", "0x741273d6": "testThrowRegisterContractAgain()", "0x8f2c44a2": "UnicornMilker()", "0x59d96db5": "terminate(uint256,string)", "0x483ba09e": "setBitcoinBridge(address)", "0x74fbbc86": "rate(uint256,uint256,string)", "0x83ea0620": "packageExists(string)", "0xd917deb5": "Donate()", "0x3fc6bc94": "payDAO()", "0x6558488a": "scheduleSetBool(address,uint256,bool)", "0x83e78b31": "bet(uint8,bool,uint8)", "0xeccb15bc": "SatPosition(int256,int256)", "0x7daa10ce": "getMyInfo()", "0x3e4565d2": "testErrorUnauthorizedNameRegister2()", "0x2143da91": "GameOfThrones()", "0xb29f0835": "doIt()", "0xdcc0ccf3": "Dao(address)", "0x70d53be5": "find()", "0x9a828a71": "oracalizeReading(uint256,string)", "0x6a6d31db": "externalEnter()", "0xf8b71c64": "rewardTo(address,uint256)", "0x0399c357": "assignFreeReadings(address,uint8)", "0x81ade307": "query(string,string)", "0xdb83694c": "getSaleInfo()", "0xa6bf3df0": "oraclize_query(string,string[2],uint256)", "0x29605e77": "transferOperator(address)", "0xf29d2f28": "setTokenHolder(address)", "0xa96f8668": "releaseTokens()", "0x8a3bc2bc": "iPropose(bytes,uint256,bool)", "0xd18611d6": "reactivate()", "0x7620a65b": "Publisher()", "0xa268b332": "testBitXorFailIndexOOB()", "0x6b1781b6": "Emergency()", "0x1003e2d2": "add(uint256)", "0x1209b1f6": "ticketPrice()", "0xe5a27038": "Pluton(uint256,string,uint8,string)", "0x22bc3b8e": "getArgument(uint256)", "0x47bdb7f4": "transferDisable(bytes20)", "0x13137731": "testThrowsUpdateLatestRevisionNotUpdatable()", "0x3f3935d1": "confirmReverse(string)", "0xecb4136e": "NotAnotherPonzi()", "0x49e347ae": "getContents(uint256[],uint256)", "0x669dafe8": "toWei(uint256)", "0xc233e870": "isLatestPatchTree(bytes32,bytes32)", "0x7b789b3d": "agreement(bytes,bytes,bytes)", "0x682d3bb0": "pdfCertificateProof(bytes)", "0x42346c5e": "parseInt(string)", "0x3177029f": "approveAndCall(address,uint256)", "0x71ffcb16": "changeFeeAccount(address)", "0xc971442c": "getDBs()", "0x362e2565": "returnDeposits()", "0xe10e274a": "CrazyEarning()", "0x6d705ebb": "register(address,uint256)", "0xbe9a6555": "start()", "0x1ce624d6": "Crypted_RPS()", "0x2c4cb4be": "removeRegistryFromNameIndex(address)", "0x68742da6": "withdrawFunds(address)", "0x18f3fae1": "setOversight(address)", "0x061ea8cc": "countByOwner(address)", "0xd6d22fa4": "MetaCoin()", "0x85654c9c": "setMembershipRoster(address)", "0x8aa33776": "setMsgPrice(uint256)", "0x4dd850fb": "UfoPonzi()", "0x07e00bcb": "kissBTCCallback(uint256,uint256)", "0xa1b7ae62": "setdirectorName(string)", "0xb4d9cc3a": "profitDisperser()", "0x0f24f5c8": "doTransfer(address,uint256)", "0x8d72a473": "deductFunds(address,uint256)", "0x28f03554": "ProcessDividend()", "0x98391c94": "muteMe(bool)", "0x346cabbc": "scheduleCall(address,bytes4,uint256,bytes,uint256)", "0xa42e36c6": "scheduleTransaction(address,bytes,uint8,uint256[5],uint256)", "0x21b36a08": "setFee(uint64,uint256)", "0xd94073d4": "PT()", "0xe8580dd4": "Survey(address,uint256,string,bytes32[])", "0x1f0c1e0c": "getEventTokenAddress(bytes32,uint256)", "0xce8b7151": "isHF()", "0x9bee757b": "requestExecution(bytes,uint256)", "0x775dec49": "keccak()", "0x6673ce2b": "Results_of_the_last_round()", "0x9f87acd0": "exec(bytes32,bytes32,uint256)", "0x02394872": "getLastBlockHeight()", "0x615664ba": "Market()", "0x0d7af726": "addGame(address,string,string)", "0xf4aa1291": "withdrawFundsAdvanced(address,uint256,uint256)", "0x8ed67a44": "setPrice(uint16)", "0x84ebde52": "Under_the_Hood()", "0x5a28340a": "accessOperatingBudget(uint256)", "0x9a89ad65": "within6Confirms(int256,int256)", "0xdfce8ac3": "fipsLegacyRegister(bytes20,address,bytes)", "0x73f310df": "multiAccessRemoveOwner(address)", "0x4cbee813": "logout(string)", "0xd992bd5b": "testResultNotZero()", "0x05b34410": "creationDate()", "0xfed4614b": "funeral(bytes,int256)", "0x58cb7323": "MainnetETCSurvey()", "0xbb504317": "divest(address,uint256)", "0x82381c96": "WatchCurrentMultiplier()", "0xcce81927": "EtherDice(address,address)", "0x70961774": "getBlockCreatedOn()", "0x84a7b223": "Canary(address)", "0x9378a9e2": "setUInt(uint256)", "0xe4360fc8": "getFileListElement(bytes)", "0xe597f402": "create(bytes1,bytes32,bytes)", "0x95d5a1be": "SignatureReg()", "0x33ce7787": "transferInvestorAccount(address,address)", "0x46c52b1a": "blockHexCoordsValid(int8,int8)", "0x3092afd5": "removeMinter(address)", "0x30945443": "update(address,string,string)", "0xc37ff3d9": "sha(uint256,uint256)", "0x29a6f31b": "oraclize_query(uint256,string,string[2],uint256)", "0x227f9633": "addOption(string,address,uint256)", "0x38eaf913": "setDirectorNode(string)", "0xab67aa58": "transferFrom(address,address,uint256,bytes)", "0x0ce3151c": "personUpdateRelation(uint256,string)", "0x216ef940": "proxyUpgrade(address,address,bytes)", "0x76bc21d9": "fireEventLog2Anonym()", "0xf004073a": "performAction(uint256)", "0xdba7ef7d": "Bookie(address,address)", "0xa0469b02": "inputToDigit(uint256)", "0x1d007f5f": "changeDAO(address)", "0x9dcb5c65": "resultsWeightedByEther()", "0x14ab9038": "setTTL(bytes32,uint64)", "0xf4d94699": "EndowmentRetriever()", "0xe74b9d11": "safeToSubtract(uint256,uint256)", "0xd7504385": "validateToAddress(address)", "0x57e2880d": "scheduleTransaction(uint256,uint256)", "0xe73a914c": "setDAO(address)", "0xc47bc007": "add_funds()", "0x37881810": "setCallbackAddress(address)", "0x686f2c90": "collectAllFees()", "0x278b8c0e": "cancelOrder(address,uint256,address,uint256,uint256,uint256,uint8,bytes32,bytes32)", "0xfac34ff6": "throwFoo()", "0x6d98e9fc": "totalWei()", "0xb0bcc610": "scheduleTransaction(address)", "0x665bcc32": "ProcessGames(uint256[],bool)", "0x3fd1f232": "LookAtAllTheseTastyFees()", "0xdd727ea6": "runJackpot()", "0x0acc4382": "getMinDailyWithdrawLimit()", "0x46b207b8": "checkExpiry()", "0xde5d953a": "logSingleIndex(bytes,bytes,uint256)", "0xf504e0da": "load_level(uint16)", "0x4b63e601": "scheduleCall(address,uint256,bytes)", "0x4a71d469": "collectRev()", "0x80db79d9": "StructAndFor()", "0x090637a1": "GetPart(bytes,uint256)", "0xc003b082": "getMyPlayerID()", "0x00a7d6b3": "checkTransferFromToICAP(address,bytes32,uint256)", "0xdcf8113e": "campaignEndedSuccessfully()", "0xd1af8a5a": "LinkerExample()", "0x01fd89a4": "getFlags(bytes20)", "0xa39a45b7": "replaceOwner(address)", "0x0a3b1cd2": "setHotwallet(address)", "0x075fe877": "scheduleCall(address,bytes,uint256,uint256)", "0x3e5fd9b5": "dEthereumlotteryNet(address,address,bool,address)", "0xa6403636": "resolve(uint8,bytes32,bytes32,bytes32)", "0x0b2acb3f": "add(address,bytes)", "0x6d522b19": "multiAccessChangeRequirementD(uint256,address)", "0x4311de8f": "ownerWithdraw()", "0xa99e7e29": "register(bytes,address)", "0x1ed6f423": "changeDescription(address,string)", "0xcd50d44f": "CheckRepresentment()", "0x4c0e207a": "__outputCallback(uint256)", "0xea8a1af0": "cancel()", "0x67387d6b": "testThrowCreateWithNonceExistingNonce()", "0xdc583801": "doubleyour5()", "0xb8077e28": "getTxOrigin()", "0xbfbc793c": "computeNameFuzzyHash(string)", "0x79baa8a9": "BasicIncome_CoFund()", "0xf4dbeb9d": "getCredRanksByContents(address,uint256[])", "0x227185d6": "Send1Get2()", "0x75c4aaa6": "addUnderDog(uint256)", "0xa7abc124": "activate(bool,bool)", "0x8df554b3": "Dividend()", "0x092b25e9": "setOwner(string,address)", "0x67af26fb": "transferOtherFrom(address,address,address,uint256)", "0x4bb278f3": "finalize()", "0xd1e15045": "sendBack()", "0xa4699cad": "resetWithdrawls()", "0xb61d27f6": "execute(address,uint256,bytes)", "0x9772c982": "scheduleCall(address,bytes4,bytes,uint256,uint256)", "0x1b3a8e6f": "directionCount(int256,int256,int256,int256)", "0xd499555b": "getFirstActiveDuel()", "0xb738169c": "betOnOddEven(bool,bool)", "0x411c4e72": "ModifyFeeFraction(uint256)", "0x06f36cc9": "helpBlue()", "0x9e65c7e5": "updateLatestRevision(bytes20,bytes)", "0xb47fa7e0": "DepositLimit(uint256)", "0xf1736d86": "m_dailyLimit()", "0x62ea82db": "bids(address)", "0x4166c1fd": "getElevation(uint8,uint8)", "0x8702735c": "setCapitol(uint256,uint256)", "0x3cc7790a": "GSI()", "0x83f6d9a4": "validateNameInternal(string)", "0x8d99b2eb": "endPoll()", "0x8bbda7e3": "setContent(string,bytes)", "0x52efea6e": "clear()", "0x2581c674": "testBitsOrFailIndexOOB()", "0x05d87fe2": "issueLetterOfCredit(uint256,uint256,uint256)", "0xcbf0b0c0": "kill(address)", "0xf83d96c1": "InsuranceAgent()", "0x8dd5e298": "canEnterPool(address)", "0x2d580ef6": "add(address,bytes32)", "0xeeda149c": "Register(address)", "0xcc25decd": "SampleOffer(address,bytes,uint256,uint256,uint256,uint256,uint256)", "0x428d64bd": "getShares(address,bytes32[])", "0x3c9a4baa": "requestOutput(bytes)", "0x8cae1374": "editBlock(uint8,uint8,uint256,int8[5])", "0x419db07b": "generousFee()", "0x202e3924": "getOperation(uint256)", "0x5ee345e4": "computeEndowment(uint256,uint256,uint256,uint256,uint256,uint256)", "0x7df23b6a": "ReleaseOracle(address[])", "0x9b2ea4bd": "setAddress(string,address)", "0x65093661": "newCommunity(address)", "0x33637d5a": "getPendingBlock(uint256)", "0x7910085d": "fipsIsRegistered(bytes20)", "0x730720b8": "testControllerValidTransfers()", "0xb0c80972": "setBalance(uint256,bool)", "0xdcf537b1": "multiply7(int256)", "0xdf5cc291": "get4(bytes,uint256)", "0x9ae4e388": "ChangeClientTokenAccount(address,bool)", "0x3121369d": "validateRequiredStackDepth(uint256)", "0x1747dfd4": "ContractPlay()", "0x598647f8": "bid(uint256,uint256)", "0xc368109c": "monster_hp(uint256)", "0x7fa22001": "assertEq0(bytes,bytes,bytes)", "0x8e280dce": "findNextYear(uint256,bytes)", "0x39d1f908": "actualBalance()", "0x8143f8a8": "totalGas(bytes)", "0xfe55932a": "setName(uint256,string)", "0x0fbf7151": "startsWith()", "0x4f20f35a": "payExpenses(address,uint256)", "0x705eeb90": "MultipleConstructorTest(bool)", "0x2df8e00d": "becomeMortal(uint256)", "0x645dce72": "updateRelease(uint32,uint32,uint32,bytes20,bool)", "0x1f6e5117": "getCallbackAddress()", "0xf51cbc72": "Level()", "0x64edfbf0": "purchase()", "0x35930e13": "setMinimalRewardedBalance(uint256)", "0x015e4f3a": "getConfigUint(int256,bytes)", "0x2c329e99": "Last_block_number_and_bloctime_used()", "0x6f3a7561": "SimpleAuction(address)", "0x6de00927": "GetUserRank(uint8,address)", "0xbe600276": "move(uint16)", "0x27d6c032": "unregister(bytes)", "0x4188d79c": "releaseExists(string,uint32,uint32,uint32,string,string)", "0x7ba38916": "changeAdminFromBoard(address)", "0x3369dace": "flipTheCoinAndWin()", "0xfa8dc33a": "checkRecordExists(bytes)", "0xebaf7f2f": "returnReward(uint256)", "0xc88961da": "createKingdom(string,address,address,address)", "0x21970c0c": "pay_royalty()", "0xb4a5ef58": "updateDefaultTimeoutPeriod(uint256)", "0x57bcccb6": "revokePermanentApproval(address)", "0xd1d1c8ae": "ConvertNumbers(bytes)", "0xc1c0e9c4": "exec()", "0xcc131be1": "CreateNewDraw(uint256)", "0x75f96ead": "Guess(uint256)", "0x8a5fb3ca": "currentFeePercentage()", "0x550bcd8d": "testThrowUpdateLatestRevisionEnforceRevisions()", "0xa6780857": "fireEventLog0Anonym()", "0x2d0104a5": "updateFirstDuel1(uint256)", "0xcbf1304d": "balances(address,uint256)", "0xdda9939c": "Store(address[])", "0xf41bfa9e": "mint(int256,uint256,string)", "0x044215c6": "token(uint256)", "0x1f903037": "getBytes32()", "0xa6f9dae1": "changeOwner(address)", "0xf9391d24": "AllPayAuction()", "0xabebb7f3": "MarketsContract()", "0x9e1e6528": "uncertify(address)", "0x81788e2b": "addAllowedAddress(address)", "0x4f44728d": "ownerChangeOwner(address)", "0x3da0ac79": "compare()", "0x96e438a1": "reclaimDeposit(uint256)", "0x5fe22c8b": "testFailTransferWithoutApproval()", "0x6835f32c": "build(bytes)", "0x5cac8b27": "amazing()", "0xad605729": "getParticipantCount()", "0xb6294bde": "AdminGetFee()", "0xec81e22e": "returnmoneycreator(uint8,uint256)", "0xc535165f": "revealAndPayout(bytes,bytes)", "0x6e0bd282": "destroy(bytes32)", "0xdda44b10": "buyRecipient(address,uint8,bytes32,bytes32)", "0xd4859dbc": "UniversalFunctionSecure(uint8,bytes32,bytes32,bytes32,bytes32,bytes32)", "0xd2602930": "RockPaperScissors()", "0xa08b3367": "EC()", "0x92d66313": "getYear(uint256)", "0xe49dcee9": "fixTokens()", "0x36555b85": "add(string,uint256)", "0x25010816": "get_length(uint256,uint256)", "0x610d5de8": "validateEndowment(uint256,uint256,uint256,uint256,uint256)", "0x79ce9fac": "transfer(bytes32,address)", "0x3ced516c": "descriptionHashes(bytes32)", "0xcf69df28": "getDataRequestLength()", "0x706dfe54": "getIssueState(uint256,bytes32)", "0x5af77fff": "Contract()", "0x66e5cb50": "stopTransfer(uint256)", "0x5f72f450": "check(uint256)", "0xf3b50c04": "rescind()", "0x57aee888": "_eraseNodeHierarchy(uint256,bytes32[],bytes32)", "0xaacc5a17": "getRandom()", "0x40275f85": "getPersonalDepositAddress(address)", "0x75700437": "query1_withGasLimit(uint256,string,string,uint256)", "0x6eb7b4c2": "underdogInfo(uint256)", "0x0f3eb785": "add(string,uint256,uint256,uint256)", "0xdc19266f": "Total_of_Players()", "0x9743dfc1": "jesterAutomaticCollectFee()", "0x6618b008": "cancelSellOrder(address)", "0x65538c73": "fireEventLog0()", "0xa4502cb8": "setExportFee(address,uint256)", "0x97bb2a63": "newvow(uint256,address)", "0xb400d149": "betOnNumber(uint8)", "0x030d406b": "entryPayout(uint256)", "0x1d71a1cd": "newIncome(string)", "0x85dd2148": "getSaleDate(bytes16)", "0x29917954": "exitPool()", "0xa25057de": "_transferToICAP(bytes32,uint256)", "0x24fc65ed": "getId(uint256,uint256)", "0x938199a5": "getDateOfLastPayment()", "0x04bb754c": "TradeFinancing()", "0xe37aa618": "distributeValue()", "0x547916ea": "finishRound()", "0xed01bf29": "budget()", "0x95ee1221": "isCancelled()", "0xfe777bcd": "etherForSale()", "0xffe302d1": "setPlz(string)", "0x891de9ed": "fromTLA(string)", "0x84734476": "copyBytes(bytes,uint256,uint256,bytes,uint256)", "0xfb114f57": "oraclize_query(uint256,string,string[3],uint256)", "0xceebe28d": "repoInterfaceVersion()", "0xb0ad38c4": "buildCity(string,uint256[2],uint256[2])", "0xefa7e56b": "GameEnds()", "0xcc3471af": "maxClaimBlock()", "0xa7c5052e": "buildDSTokenRegistry()", "0x6831c169": "totalPayedOut()", "0x98f3b81a": "getShares(address,bytes32[],int256[])", "0x2d077ad0": "Latch()", "0x0ac28725": "requestTradeDeal(uint256,uint256,string)", "0xb311ee0c": "refundClaimDeposit()", "0xadd82871": "strEqual(string,string)", "0x7879e19e": "CollectAllFees()", "0x5bd74490": "regProxy(address,address)", "0xd2b0d554": "getDisclaimer()", "0x0b74edc6": "testFinalHash()", "0x6cf761d4": "getMinConfirmationsByAddr(address)", "0x4cedf74e": "get_party1()", "0x4adcbd19": "isThisHardforkedVersion()", "0xefdecd9b": "check_withdrawdao()", "0x996a8046": "__callback(bytes32,string,bool)", "0x7c9cd7df": "changeDeveloper_only_Dev(address)", "0x3f77b560": "newDocument(bytes)", "0x06b5f02d": "calcWinnings(uint256,uint256)", "0x0a2282ae": "JackPot()", "0x378a2178": "tallyVotes()", "0xd8915fc5": "DCAssetBackend(bytes32,bytes32)", "0x0f590c36": "emergencyFixGameResult(uint64,uint256)", "0xea4af029": "ConferenceCertification()", "0x769dc523": "GetCategoryNumber(bytes4)", "0xd5df7559": "removeDocument(uint256)", "0x749aa2d9": "endRound()", "0xd8e5ae6a": "Etheramid()", "0xc0576b73": "monsters(uint256)", "0x32fefb4c": "add_account(address,address)", "0x7d619d9b": "holdCoin(address,address)", "0x5b067cce": "testCreateCostMain()", "0x384b1393": "follow(uint256)", "0x4162169f": "dao()", "0x5d8227e6": "FactoryBase(string,string,string)", "0x6bf52ffa": "Vote()", "0xeb5904c0": "setProfitDistributionContract(address)", "0x366a68dc": "setBlockLock(uint256)", "0x80d9eaa6": "refCount()", "0x89b8b492": "read(uint64)", "0x46b5e202": "set_num_levels(uint256,uint256)", "0xd96de4ce": "AdminDrawError()", "0x47b47102": "bakeCookie(string)", "0x1d7b5baf": "setUint(int256,bytes32,string,uint256)", "0x0699d07d": "updateMaxVal()", "0xfa544161": "getOwner(address)", "0x638560cf": "registerBool(address,bool)", "0x7c25f260": "Government()", "0x24a852c6": "unset(bytes)", "0xa32f0f41": "testFailControllerUnapprovedTransferFrom()", "0x0968f264": "withdraw(bytes)", "0x5f52e9fd": "WithdrawCashForHardwareReturn(uint256)", "0xc0f68859": "getMinimumGracePeriod()", "0x1bf6c21b": "USD()", "0x0fe234ed": "testSetController()", "0x05a17fc6": "getAccountFeed(address,uint256,uint256,uint256)", "0x673448dd": "isApproved(address)", "0x59dac714": "hashTo256(bytes)", "0x5a09f2f4": "setHouseFee(uint256)", "0x013cf08b": "proposals(uint256)", "0xeebf9808": "PiggyBank()", "0xadd43c59": "EtherTopDog()", "0xf909d60d": "getMinimumGasLimit()", "0xeb045789": "ChannelSeries(address)", "0x66d38203": "setup(address)", "0xe8641652": "strCompare(string,string)", "0x1959a002": "userInfo(address)", "0x737c8ea1": "_getRevisionBlockNumber(bytes32,uint256)", "0x127714c7": "getBudget()", "0x97daa043": "register(bytes,address,address,uint256,bytes)", "0xb5784f6f": "testMultiplePackages()", "0x0ce46c43": "scheduleCall(address,bytes4,bytes,uint16,uint8,uint256[5])", "0xe5782fd5": "setFeeStructure(uint256,uint256,uint256)", "0xa9f6def0": "HonestDice()", "0xeb7cdb56": "rankDown(uint256,uint256)", "0xb17b94c1": "testSystem()", "0xdd36e18b": "ContractStatus()", "0xee0dc478": "testSetEnforceRevisions()", "0x918359c6": "needsBirth()", "0xa5b1e13d": "settle(address,address,uint256,uint256)", "0x6b76484e": "swap(address,address)", "0x68402460": "scheduleCall(address,bytes4,uint256,uint256,uint8,uint256)", "0x733480b7": "transferToICAP(bytes32,uint256)", "0x567dbf18": "__forward(address,uint256,uint256,bytes)", "0x73e1743a": "buildDSBasicAuthority()", "0x482961e1": "updateReading(uint256,uint256)", "0x4e6ba0a9": "testCreateCostMultisig()", "0x8d7108e5": "isValidLocation(uint8,uint8,int8[5],int8[24])", "0x10142785": "assign(bytes,uint256,bytes1)", "0xfe97ee88": "hasPhone(address)", "0xe2861c8d": "cashOutProfit()", "0x0fa9ced4": "emergencyFuneral()", "0x8389f353": "setNumCities(uint256)", "0xdba1ac3d": "getEnforceRevisions(bytes20)", "0x1b4fa6ab": "getDefaultStackCheck()", "0x79be02af": "Read(address)", "0x70844f7a": "sendBadge(address,uint256)", "0x7bfaad96": "addNode(bytes,address)", "0x4d782cbc": "executeSellOrder()", "0xbe71248a": "payWinner()", "0x41304fac": "log(string)", "0x4f059a43": "getClaimAmountForBlock()", "0x6d2cb794": "airaTransfer(address,address,uint256)", "0x5a5383ac": "canExitPool()", "0xcabd27de": "Motion(address)", "0x433d4aab": "resolve(uint8,uint8)", "0x7d89ae63": "__findRef(string)", "0x4e1053cc": "RobinHoodPonzi()", "0x0220a5b4": "terminate(string)", "0x419ffa03": "fipsRegister(address)", "0x77a7e6be": "getRefTotal(uint256)", "0xed64bea4": "JamCoin()", "0x3cf885c4": "isBitSet(uint256,uint8)", "0xf2da67db": "setMany(uint256,int256,uint256,bytes20,address,bytes)", "0xe26c8434": "AdminStartDraw(string,bytes)", "0x13df7091": "mintAll(int256)", "0x8a46bf6d": "testFallback()", "0x29bed3bf": "EthereumRoulette()", "0xf869b11a": "declareVictor(uint256,uint256)", "0x45c41478": "getMarkets(bytes,address)", "0x90cf581c": "voteYes()", "0x9ec35352": "returnRandom()", "0x5025b9ae": "expire(uint256,uint256,uint8,bytes,bytes,bytes)", "0x338a1379": "_setPackedBlockNumber(bytes20,uint256)", "0xdb641ab4": "Game_balance_in_Ethers()", "0xbc0e7adb": "testThrowsDisownNotOwner()", "0x302d350e": "firstChainedCallback(uint256)", "0x5af73f3f": "getMinimalBalance(uint256,address)", "0x4c488dac": "getChannelValidUntil(bytes)", "0xa1616429": "testBitOrSuccess()", "0xaa64c43b": "transferPool(address,address,uint256)", "0x78e97925": "startTime()", "0xa0355f4e": "decline(uint256)", "0x02556de3": "updateMajorTree(bytes32)", "0x01984892": "name(address)", "0xfad4b99a": "updateChannelMinimum(address,uint256)", "0x4f39ca59": "drop(bytes32)", "0x61591a7c": "personUpdateDOB(uint256,int256)", "0x17b3a34b": "_addIdentities(uint256,bytes32[])", "0x91d8b14e": "BuyTickets()", "0x1aadcc34": "convertGreyGreen(uint8,uint8)", "0x9a863892": "NewProposal(uint256)", "0xc5487661": "proxyTransferToICAPWithReference(bytes32,uint256,string)", "0x85f8c16d": "claimHours(int256)", "0xa71f94c8": "scheduleSetUInt(address,uint256,uint256)", "0x7ef09476": "transfer(uint64,address)", "0x94bcdb4c": "Example2()", "0x37930615": "extend(bytes16[],uint64)", "0xfb09b1ac": "testBalanceOfReflectsTransfer()", "0x19a278b9": "getBAddress()", "0xc0a239e3": "valuePerShare()", "0xa039e3c7": "testSetNotTransferable()", "0x22593300": "Small(address)", "0xe21608be": "ReserveToken()", "0xc2985578": "foo()", "0xb463bcde": "testThrowsSetNotTransferableNotOwner()", "0x88d695b2": "batchTransfer(address[],uint256[])", "0x37b0574a": "isClassic()", "0x1da6822c": "testThrowsTransferEnableNotTransferable()", "0xdcfa9cc0": "testProxyCall()", "0x478aa69e": "unauthorizeUser(address)", "0x102accc1": "fireEventLog2()", "0xed62cf1f": "setCanCall(address,address,bytes,bool)", "0x15f73331": "invalidateName(string)", "0x73e30e49": "majorEventFunc(uint256,bytes,bytes)", "0x00c721ab": "setHand(uint256)", "0xf9e27106": "investmentEntryCost()", "0x4c7f74df": "EtherDelta(address,address,address,uint256,uint256,uint256)", "0xb74e452b": "today()", "0xd3118a5a": "addDoc(string,string)", "0xc204f9f1": "_transferFromToICAP(address,bytes32,uint256)", "0xf50d3914": "resetFoundationtList()", "0xe67cdfb7": "moveOldUser(uint256)", "0x98eaca94": "inKissBTC(uint256)", "0xc633084f": "sendGreeting(address,string)", "0xde10f04b": "eraseNode(bytes32[])", "0xd50495f4": "addTransaction(bytes)", "0x96cff3df": "getMinimumCallCost(uint256,uint256)", "0xce373b95": "heroOfThePit()", "0x39e525f9": "resolveCallback(uint256)", "0x942b90d3": "getRewardTable()", "0xedca914c": "buyTicket()", "0x5fcb568c": "release(string,uint32,uint32,uint32,string,string,string)", "0x6c9c2faf": "getSupply()", "0xf1448e10": "requestExecution(bytes)", "0x0c08bf88": "terminate()", "0x08aba5aa": "setAccountBalance(uint256)", "0x2c46d8d5": "EndRound(uint256)", "0x3d5db1c2": "incrUserOnholdBal(address,uint256,bool)", "0xf2f254c7": "getLatestMinorTree(bytes32,uint32)", "0x373a1bc3": "scheduleCall(address,bytes4)", "0x3a96fdd7": "compare(string,string)", "0x738ddabe": "getContentIndexedAccountCred(uint256,address,address)", "0x5acce36b": "getEndowmentBalance()", "0x1ca60aeb": "setMeltingContract(address)", "0x52375093": "m_lastDay()", "0x565a2e2c": "getBeneficiary()", "0x9d5c6061": "getMsgGas()", "0x41d31feb": "get_read_only_keys()", "0x796b89b9": "getBlockTimestamp()", "0x4a41e045": "getUint8(int8)", "0x38e48f06": "save(string)", "0x1cda37f2": "eraseRecords(bytes32)", "0xae978f08": "getLatestTweet()", "0x20909fa0": "communityCurrency()", "0xafbec8df": "TheGrid()", "0x1c14179a": "GavCoin()", "0x0b6142fc": "breach()", "0x3ab1e703": "roundMoneyDown3SF(uint256)", "0x414ceac0": "investorAddFee(uint256)", "0x82a62137": "activateAccount(address)", "0x4ca7fbd0": "updateTokenPriceWeekTwo()", "0x2551858e": "getFlags(bytes32)", "0x4ad07b0e": "oracleOutcomes(bytes32,address)", "0x60b431a4": "testGetSig()", "0xa5f8cdbb": "buyTicket(address)", "0x64aabe92": "tryExec(address,bytes,uint256)", "0xa6c01cfd": "isInGeneration(uint256)", "0x149c5066": "ChanceOfWinning(uint256)", "0xc068eae0": "player_collect_winnings(uint256)", "0x8129fc1c": "initialize()", "0xcf832ce2": "ownerRefundPlayer(bytes32,address,uint256,uint256)", "0x3517a740": "getNodeParent(bytes)", "0xec6afc22": "oraclize_query(uint256,string,string[3])", "0x50944a8f": "setMembership(address)", "0x85b1423e": "returnAll()", "0xd95a2d42": "lendGovernmentMoney(address)", "0x347632e8": "getShareholderAdressByID(uint256)", "0xbb39a960": "trade(address,uint256,address,uint256)", "0x8abadb6b": "setAccountLevel(address,uint256)", "0xa502aae8": "getNextGenerationId()", "0xb5bfdd73": "addDSource(string,bytes1,uint256)", "0x28d3ad3f": "getPot(uint256)", "0x08933d11": "getJoinBlock(address)", "0x8383bfc8": "EscrowFoundry()", "0x2ca15122": "sign()", "0xf340fa01": "deposit(address)", "0x9ed93318": "create(address)", "0xa1c0539d": "scheduleCall(address,bytes4,bytes)", "0xced92670": "changeMultiplier(uint256)", "0xb2c652f3": "getMarkets(uint256[128])", "0x69b144eb": "testThrowsCreateNewRevisionNotOwner()", "0x16c72721": "forked()", "0x712ca0f8": "getOrder(string)", "0x0cf45ba5": "updateFirstDuel2(uint256)", "0x4173b181": "setWeiPrice(uint256)", "0x689b3e2d": "Moonraker(address,address)", "0x8691162a": "TlcCoin()", "0x432ced04": "reserve(bytes32)", "0x38178fbe": "addString(string,string)", "0x8f1327c0": "getRound(uint256)", "0xa9eed530": "reduceOrderQty(uint256,uint256)", "0x408938d0": "testUpdatePackageDb()", "0x56105a08": "DgxSwap()", "0xc43d0575": "scheduleCall(bytes4,uint256)", "0xdba21657": "askForEther(uint256)", "0xca3b5c91": "hasRelation(bytes,bytes,address)", "0xc71cbcf3": "recoverAccount(address,address)", "0xb010d94a": "canExitPool(address)", "0x0a16697a": "targetBlock()", "0xff1f7046": "requiresAuction(string)", "0x0b811cb6": "executeProposal(uint256,bytes32)", "0xbb8be064": "HardwareToken()", "0xe2b05077": "getSaleDate(bytes,uint256)", "0x1e9a6950": "redeem(address,uint256)", "0xd21b84ac": "createNewDAO(address)", "0xd644e356": "index(uint256,address,uint256,uint256)", "0xea27a881": "getMinimumEndowment(uint256,uint256,uint256,uint256)", "0x99a88ec4": "upgrade(address,address)", "0xc8e4acef": "playerByAddress(address)", "0x0b7abf77": "TOTAL_TOKENS()", "0xfb5d7376": "step4()", "0xc0aa18e7": "History()", "0xe2233ada": "smartDoor(address[])", "0xd6006e88": "send(address[],uint256[],uint256)", "0x95671958": "getFileListTail()", "0x16bac350": "overthrow(string)", "0x5cb18a6d": "fipsLegacyRegisterMulti(bytes20[],address,bytes)", "0x60116397": "Registrar(address,bytes32,uint256)", "0x60fe47b1": "set(uint256)", "0x5f8f0483": "buyBankerAgreementFromImporterBank()", "0x4c8cc20b": "toContentID(address,string,string,address,uint256)", "0x45ca25ed": "changeName(address,string)", "0xb21bce4c": "vote(bytes,bool)", "0x334dc700": "CanaryV7Testnet()", "0xc31d0031": "CrowdFundDAO(string,uint8,string)", "0xf3d91708": "isEligibleForUpgrade(address)", "0x0ee07836": "adjustDifficulty(uint256)", "0xf6232556": "Security_GetNumberOfAttemptsToConnectBankAccountToANewOwnerAddress()", "0xb2d37e95": "remove_order(uint32)", "0x691d58e7": "_applyRefund(uint256)", "0x1c2353e1": "isCertifier(address)", "0xcf158fe9": "scheduleTransaction(uint256,uint256,uint256)", "0x5d96ec65": "setAdministrator(address,string,bool)", "0x0651844e": "activateBalance(address)", "0x217311ac": "getWords(uint64)", "0xc127c247": "addMember(address,string)", "0x40c0bcb9": "checkBetNumber(uint8,address,bytes32,bytes32)", "0xb633620c": "getTimestamp(uint256)", "0x5b764811": "_jMul(uint256,uint256,uint256,uint256)", "0xfe029156": "swap(address,address,uint256,uint256)", "0x31db4b95": "doTriggerAuth()", "0x203c03fa": "Coinflip()", "0x209a5b8a": "moneySumAtSettlement(address,uint256,int256,uint256)", "0xf10ae2ab": "__dig_then_proxy(uint256,address,bytes)", "0xd532e481": "activateFrozenAccount(address)", "0xe9a9c1b4": "get_party1_balance()", "0x8fcc9cfb": "setMinDeposit(uint256)", "0xe5c7e509": "testThrowTransferDisableNotEnabled()", "0x4e077f2a": "addGasEther()", "0xb7c93330": "ResourcePoolTester()", "0x82661dc4": "splitDAO(uint256,address)", "0x0e554bd8": "scheduleCall(bytes,uint256,uint256,uint8)", "0x49041903": "getGame(uint64)", "0x0e1da6c3": "claimTimeout()", "0xc53ad76f": "Kardashian()", "0x8b7bcc86": "numWinners()", "0x1043dcdf": "LastIsMe(uint256,uint256)", "0x6cd22eaf": "updateAuthority(address,bool)", "0xb796a339": "addRegistryIntoOwnerIndex(address,address)", "0x308d6613": "getSignData(uint256,uint8)", "0xed88c68e": "donate()", "0xb719d1d0": "getRegInfo(address)", "0xac8d6030": "removeRequest(address)", "0x46f0975a": "signers()", "0x434cb64c": "startNextGeneration()", "0x6cb3d30a": "triggerTryAuth()", "0x3c067945": "fundBalance()", "0x26c7edaa": "flip4(bytes)", "0xf76f950e": "uint2str(uint256)", "0x860e9960": "BetPriceLimit()", "0xb0ecca8f": "LookAtLastTimePerZone(uint256)", "0xa35cfa22": "make_move(uint256,uint8,uint8,uint8,uint8)", "0x3f74fecb": "DSTrueFallbackTest()", "0xdd2ad311": "scheduleCall(bytes,uint256)", "0x0ae5e739": "grantAccess(address)", "0x7d5fec5a": "setOwner(uint8,uint8,address)", "0x6a4b6aa5": "untrustedChildWithdraw()", "0x332f93a9": "nextPayoutGoal()", "0xc5ae6e0e": "Kernal()", "0x75438e49": "fillGas()", "0x51404cbe": "forceDivestOfOneInvestor(address)", "0xeacfc0ae": "Authorized()", "0xe59d843a": "Replicator(bytes,uint256,uint256,address)", "0xf00e8651": "createRequest(address[2],address,uint256[11],uint256,bytes)", "0x02acdb44": "setAnyoneCanCall(address,bytes4,bool)", "0x2a24f46c": "auctionEnd()", "0x7ef1925b": "getShareRange(uint256,uint8)", "0x2fac1a54": "newOrder(bool,uint256,uint256,uint256,uint256)", "0x56b8c724": "transfer(address,uint256,string)", "0x33fd066d": "doBalanceFor(address)", "0xf29617da": "registrationDeposit(address)", "0x2b297f9e": "registerDao(address)", "0x79cce1c5": "getReleaseHashes(uint256,uint256)", "0xbed1b8b9": "convertToInt(string)", "0xef5daf01": "_dumpToCompany()", "0x23dc42e7": "query1(uint256,string,string)", "0xa53b1c1e": "setInt256(int256)", "0xb8cf14e7": "updateStatusPlayer()", "0x61aa8d93": "processFee()", "0x10f41715": "updateMintingData(uint256,uint256)", "0x048e2e94": "getAccountSize(address,uint256)", "0x7c47965e": "isInCurrentGeneration()", "0x420a8ac8": "NanoPyramid()", "0xe56556a9": "getPlayerID(address)", "0x5cd2f4d3": "approve(address,bytes32)", "0x8da4d776": "newCommune(address)", "0x4d30b6be": "balanceOf(address,bytes32)", "0x4a606c53": "_db()", "0x4956eaf0": "deploy(address,uint256)", "0xf1fe42b8": "TransactionRequest(address[3],address,uint256[11],uint256,bytes)", "0x63e38ff3": "id_for_nym(uint256)", "0x0e757a2e": "testSetAndGet()", "0x3facd57c": "registerBill(uint256,address,address,uint256,uint256,uint256)", "0xe548cf13": "betOnColumn(bool,bool,bool)", "0x2f1e4968": "makeNewProposal(string,uint256)", "0x0b467b9b": "revoke(bytes)", "0x74bfb965": "addNewProxy(address)", "0x02de2cf3": "isLatestPreReleaseTree(bytes32,bytes32)", "0xfc1f7652": "_isBoardMember(address)", "0xefef39a1": "purchase(uint256)", "0x3ae9b510": "getLatestMajorTree(bytes32)", "0xc24924d6": "setQueryFee(uint256)", "0x839930ba": "getMinimumBet()", "0x8f5e9ca7": "acceptTOS(address,bool)", "0xd1100691": "BookCafe()", "0x839849c0": "changeBaseMultiplier(uint256)", "0x758971e8": "ownerTakeProfit(bool)", "0x2b785960": "testBitAndSuccess()", "0xd96a094a": "buy(uint256)", "0x379607f5": "claim(uint256)", "0x88e072b2": "checkTransfer(address,uint256)", "0x05fefda7": "setPrices(uint256,uint256)", "0xfc63d4fb": "order(bool,uint32,uint128)", "0x5718b994": "checkEvent(address,bytes,bytes,uint256)", "0x0c0662a8": "getLastWithdrawal()", "0xeb947f19": "ExampleResourcePool()", "0xb51c4f96": "getCodeSize(address)", "0x702fc7da": "ReviewModel()", "0xc6cb7a96": "orderMatchTest(uint256,uint256,int256,uint256,uint256,address,address,uint256,int256)", "0xb7760c8f": "transfer(uint256,address)", "0x32b12eac": "setFallback(address)", "0x0a4d564c": "TieUpLooseEnds()", "0xc3ad5ecb": "getTweet(uint256)", "0xe86afde0": "description(uint64)", "0xd0549602": "scheduleTransaction(address,uint256,uint256,uint256)", "0xbf2e694f": "getPreviousRequest(address,address)", "0x2525f5c1": "cancelBid(address,bytes32)", "0x19f02ceb": "set(address,address,uint256)", "0xf00acc47": "prepareRoll(uint256,uint256)", "0x29d28aad": "Broker(address)", "0x041d0c0b": "MyTokenLoad(uint256,string,uint8,string,address)", "0xd81ab0c1": "invoke(uint256,address,address,bytes)", "0xab09ee80": "respond(uint256,uint256,uint256,uint256)", "0xd985f122": "RelayToolsTest()", "0xbe0638e4": "WealthShare()", "0x5263ba87": "getLatestPatchTree(bytes32,uint32,uint32)", "0xb7bae9b7": "exists(bytes,bytes)", "0x0b80f8d3": "invmod(uint256,uint256)", "0xbb4d7cd1": "tag(uint256,string)", "0xadf54e0c": "betOnLowHigh(bool,bool)", "0xed54746e": "lastAuction()", "0xf158458c": "getMinimumEndowment(uint256,uint256)", "0x5fcc2edb": "IndividualityTokenRoot(address)", "0x7cc48875": "Slots()", "0x2885b593": "extractMasterKeyIndexLength()", "0x8940aebe": "publicKey(uint256)", "0x0aece23c": "getFeeAmount(int256)", "0x72c3015c": "mint(int256,address,string)", "0xd6a619e3": "transferIfPuritanical(address)", "0xe30443bc": "setBalance(address,uint256)", "0x1277e24f": "payOneTimeFee()", "0xb958a5e1": "getPhoneByAddress(address)", "0x4e71d92d": "claim()", "0x3e0d4f4a": "ApproveContractorProposal()", "0x18160ddd": "totalSupply()", "0x150ad2a8": "owner_transfer_ownership(address)", "0xa2b5591c": "oraclize_query(uint256,string,string[],uint256)", "0x8d227fc0": "getPeriodInfo()", "0x1c0b6367": "processTransaction(bytes,uint256)", "0xf245b9e4": "DVIP(address)", "0x392327b5": "owner_set_fraction(uint256)", "0xadaccd74": "getNickname(address)", "0x2e0ef395": "voteOnNewEntryFees_only_VIP(uint8)", "0x89c19ddb": "concat(string,string)", "0xcef8d343": "buyShare(uint256,bool)", "0xd224118f": "PrepareDraw()", "0x4269d8ef": "_safeSend(address,uint256)", "0xda1441cd": "KudosBank(uint256)", "0x7ccfd45a": "removeSubUser(address)", "0xcc70bb1a": "publish(string,string,string,address)", "0x708f29a6": "getTotalPayments()", "0x05459f42": "WeeklyLotteryB(address)", "0x452d44dc": "checkBothNotNull()", "0x659fb968": "getOracleOutcomes(bytes32[],address[])", "0x3570c2ee": "PosRewards()", "0xbca86986": "testSetup()", "0xff49b26e": "createEvent(uint256,uint256,uint8,uint32,address,uint256,uint8)", "0x541d920c": "commit(bytes,string)", "0xa6a20ff6": "DSEasyMultisig(uint256,uint256,uint256,uint256)", "0x0f5381f1": "testUserCanIncreaseVersionNumber()", "0xf8f46b5f": "getCurrentMinerAddress()", "0xfcfff16f": "open()", "0x5a9b0b89": "getInfo()", "0xb8017221": "get_party2_balance()", "0x514dcfe3": "seller_accept()", "0x2004dff6": "Basics()", "0x0b6d8d52": "createDAO(address,uint256,uint256)", "0xf18d20be": "adminWithdraw()", "0x8f9df278": "newEntry(int256,bool,uint256,int256,string,bytes32,address,uint256[])", "0x75949c13": "sendHalf(address)", "0x64ac2c4a": "WavesPresale()", "0x8946d33f": "SplitterEthToEtc()", "0x11400d8e": "priv_fastGetBlockHash__(int256,int256)", "0x7266f4a4": "X3()", "0xb189ad2a": "testErrorUnauthorizedAfterTransfer()", "0x31c2bd0b": "propose(address,bytes,uint256)", "0x100c8ada": "setCAmodulus(bytes)", "0x296ed88f": "testFailControllerInsufficientFundsTransferFrom()", "0xd5dbb1ad": "solveBet(address,uint8,bool,uint8,bytes32,bytes32)", "0x8a9ffb90": "transfer(string,string,bool)", "0x968908a3": "createMarketMaker(uint256,uint16,uint256)", "0x7b02b2c9": "sendMsg(address,string)", "0xa33dd801": "setTreasuryBalance(uint256)", "0x2f553d31": "isCreated(bytes32)", "0xf712d7ff": "testFailControllerTransferFromWithoutApproval()", "0xe51ff1fc": "iterateOverThings()", "0x60fd902c": "gnosisToken()", "0x2ef875fb": "div10(uint256,uint8)", "0x640f244b": "findSuitableGen()", "0x16cb9a01": "assertFalse(bool,bytes)", "0xe671f510": "onEtherandomExec(bytes32,bytes32,uint256)", "0x758b5172": "setPlayersPerRound(uint256)", "0x6423db34": "Reset()", "0x21958a50": "AddressSeries(address)", "0xfb87d5ea": "TransactionRequest(address[4],address,uint256[11],uint256,bytes)", "0xfb279ef3": "tip(uint256,address,uint256)", "0x338cdca1": "request()", "0x4e7ad367": "fireEventLog1Anonym()", "0xbd9335c0": "scheduleHangouts()", "0x4cb85356": "BranchSender(uint256,bytes32)", "0x1d7e1f68": "getContentRank(address,uint256)", "0x1a1df394": "Play(bool)", "0x468129a5": "setUnit(uint256,uint256,uint256)", "0xecb70fb7": "hasEnded()", "0x2d49ffcd": "getLocked()", "0x2e06c756": "post(string,string,string,uint256,uint256,address)", "0x73f93a48": "getAccountContentTip(address,uint256)", "0xf6a3d24e": "exists(address)", "0x5fbddcf3": "isLivingMonarch()", "0x6d568c43": "weiToCents(uint256)", "0xacf4280c": "buildDSApprovalDB()", "0xf3541901": "execute(address,bytes,uint256,uint256)", "0x88eb7af7": "_isHuman()", "0x48a490fb": "transferFromTreasury(address,uint256)", "0x5e03d393": "setAccountFrozenStatus(address,bool)", "0xfc687311": "betOn(int8)", "0x5bbfe9b6": "_myGroupHelper()", "0x5629c6d9": "doExecution(address)", "0xe3a9b508": "EnableDisableTokenProxy()", "0x9229c504": "new_mainPlayer(address)", "0x6f6c0244": "generateShortLink()", "0x33613cbe": "getBondBalance(address)", "0x4229616d": "collectPercentOfFees(uint256)", "0x4ed3885e": "set(string)", "0x043bb5e7": "getIdentities(address[])", "0xad2fea7c": "removeMinter(int256,address)", "0x0b7e9c44": "payout(address)", "0x17f5de95": "MAX_TOKENS_SOLD()", "0x50ea1932": "lookupISO3116_1_alpha_2(bytes)", "0x96f7807a": "getDuel2(uint256)", "0xa97ffd5e": "safeToSell(uint256)", "0x2f4ee5d4": "registerThrone(bytes,uint256,address,uint256,uint256)", "0x4c0bcfe5": "getTransferableBalance(address)", "0x0d17bc2e": "_disallow()", "0x0ca7395f": "returnFund(address,uint256)", "0x69fe0e2d": "setFee(uint256)", "0xfaf27bca": "greeter(string)", "0x0c7de59d": "edit(address,bytes,bool)", "0x16e27349": "getFeeRecipient(int256,int256)", "0x37751b35": "doTransfer(address,address,uint256)", "0x67fc1c6a": "validateProposedMonarchName(string)", "0xf59f99ee": "createNextGeneration()", "0x6be505f5": "selectWinner(bytes32)", "0xf6bd5893": "getGas(uint256)", "0x35b09a6e": "someFunction()", "0xb3aaa277": "validate(address[4],address,uint256[11],uint256,bytes,uint256)", "0x4f052648": "XaurumDataContract()", "0x117b4705": "retract(bytes32)", "0x2145e36c": "testBitSetFailIndexOOB()", "0x3d750b28": "found()", "0x1334a5e2": "eventCallback(uint8,address,address,uint256)", "0x3c2c21a0": "scheduleCall(address,uint256,bytes4)", "0x82996d9f": "rent()", "0xaf640d0f": "id()", "0xdaf22f4d": "identify(bytes32)", "0xfe4667e9": "getMaxLossAfterTrade(address,uint256,uint256,int256,int256)", "0xfc108f70": "GamblerPerAddress(address)", "0x89f4ed7a": "getLastTag(uint256)", "0xfcc11241": "addOrder(uint256,uint256,uint256,uint256,uint256,uint8)", "0x43243797": "fundsOf(address)", "0x892c0214": "NumberOfCurrentBlockMiners()", "0xb5a6c525": "extractFrozenAccountLength()", "0x1acb2719": "getNextRequest(address,address)", "0xa89a4f09": "creatorBalanceChecker()", "0x1e83409a": "claim(address)", "0x5e1d7ae4": "changeFeeRebate(uint256)", "0xb7482509": "deposit(address,string)", "0xfb47a067": "_getRevisionBlockNumber(bytes20,uint256)", "0x5dcdddd1": "testSafeToAddFix()", "0x9aa26f06": "registerBytes32(address,bytes)", "0xd085e66e": "GetPart(bytes32,uint256)", "0x2cd78450": "activateExportFeeChargeRecord(address)", "0x35d129f6": "untag(string)", "0x1a7a98e2": "getDomain(uint256)", "0x877653f0": "_storeBalanceRecord(address)", "0x446fbcd1": "CredSign()", "0xfae8f9a2": "setInitialParent(int256,int256,int256,int256,int256,int256)", "0xc1b056b0": "getNodeLeftChild(bytes)", "0x71f297cc": "XaurumToken(address)", "0xe3ffc9a3": "sendEtherToOwner()", "0xeccf1b29": "CrystalDoubler()", "0x57f4d5ec": "processDividends(address,uint256)", "0x75c589a0": "getMinimumCallCost()", "0x66772438": "computeResponse(uint16)", "0x7fefde53": "WillRegistry()", "0x8f4fb958": "calculateRandomNumberByBlockhash(uint256,address)", "0xed498fa8": "userTokens(address)", "0x5601eaea": "execute(uint256,uint256)", "0x8dd8596c": "sendDonation()", "0x15a0df43": "testThrowCreateNewRevisionNotOwner()", "0x0382c254": "CheckHash(uint8,uint8,uint8,uint8,bytes32)", "0x157f8f51": "feePaid(int256,int256,int256,int256)", "0xf00aac7f": "ArrayRR()", "0x7b7d7225": "_approve(address,uint256)", "0x54ed7b6e": "addHash(bytes)", "0x235c002d": "transferOther(address,address,uint256)", "0x7057c20d": "CFD(address)", "0xd5563f31": "createAuction(uint256)", "0x46c3166f": "testThrowRetractLatestRevisionNotOwner()", "0x4420e486": "register(address)", "0x9a969768": "distributeProfits(uint256)", "0x464f37c9": "trustedChildRefund()", "0x5d495aea": "pickWinner()", "0xdf55b41a": "owner(string)", "0x10e6e06c": "vote(bool,uint256)", "0xe7faecec": "testFailInsufficientFundsTransfers()", "0xea98e540": "proxyTransferFromToICAPWithReference(address,bytes32,uint256,string)", "0xfff78f9c": "doThrow()", "0x9bb01b5f": "ElcoinDb(address)", "0xdc6dd152": "playerRollDice(uint256)", "0x5d0be9de": "softWithdrawRevenueFor(address)", "0xcd591822": "CanaryV7Fast()", "0x36e6b92e": "taskProcessedWithCosting(uint256,uint256)", "0x7bb6a4c6": "uno(uint256)", "0x03427656": "getDefaultSoftResolutionBlocks()", "0xc1fd4339": "createMarket(bytes32,uint256,uint256,address)", "0xeb95b7d5": "Bounty(address,address)", "0x4dd49ab4": "get(bytes,uint256)", "0xfa6d373c": "LeaderHash()", "0x8c8d98a0": "toTimestamp(uint16,uint8,uint8)", "0x9a79f4a8": "testFailHeaderInsufficientFee()", "0xdd90c403": "getAccountFeed(address,uint256,uint256)", "0x58e59c32": "get_entry(uint256,uint256,uint256)", "0xfd747c0b": "rsaVerify(bytes,bytes,uint256,bytes)", "0x0f4cf692": "numMessages()", "0x18433bb7": "DrawPrepare()", "0x1dea0c57": "getRealBet(uint256)", "0x7d60e343": "getFileListSize()", "0xd24ddcfe": "buyKissBTC()", "0xa055fe64": "_projectCommitNew(address)", "0x5f17114e": "TimeDeposit()", "0x85fe0448": "testThrowRestartNotUpdatable()", "0x901717d1": "one()", "0x528fd7b0": "manualPayExpiredDuel()", "0x85952454": "newOwner(address)", "0xf34c7010": "commitSecurity(address,uint256,uint256)", "0x3bf2313d": "__transferToICAPWithReference(bytes32,uint256,string)", "0x67b830ad": "fillOrder(uint256)", "0x73fac6f0": "confirmReceived()", "0xf1b3f968": "getRaceEndBlock()", "0xf99fc046": "dEthereumlotteryNet()", "0xb409da05": "logDoubleIndex(bytes,bytes,bytes,uint256)", "0x9e920587": "testOwnedAuth()", "0x8e7cb6e1": "getIndex(uint256)", "0xe2f8a017": "payInstallment(uint256)", "0xac3e6b2f": "testSetNotRetractable()", "0x8fbc3ecd": "BUFFER()", "0x4b729aff": "buyNumber(uint256)", "0x166c4b85": "len(bytes32)", "0x6299f8cf": "stop(uint256)", "0xd767aee0": "bbb()", "0x29090202": "Resolver(address)", "0xcc2c2bcf": "MotionFactory(string,string,string)", "0xfd260dfc": "getCertificationDbStatus(address)", "0x30aceb89": "validateRequestParams(address[3],address,uint256[11],uint256,bytes,uint256)", "0xd11f13df": "numberOfParticipantsWaitingForPayout()", "0xd05c78da": "safeMul(uint256,uint256)", "0x69953501": "setUtils(address)", "0xff7f5f2a": "EtherizationUtils2()", "0xde4b3262": "setBasePrice(uint256)", "0x6cf9cc58": "registerResource(bytes,uint256,bytes,string)", "0x7ac37d58": "ownerTransferEther(address,uint256)", "0x0121b93f": "vote(uint256)", "0x07b6f631": "testTestHarnessAuth()", "0x869b3f6a": "testThrowsRetractNotOwner()", "0x18253234": "ticketsAvailable()", "0x5581004d": "createThrone(bytes,uint256,uint256,uint256,uint256)", "0x7102c138": "Standard_Token(uint256)", "0xce5566c5": "cash(uint256,uint256)", "0x16a25cbd": "ttl(bytes32)", "0x0c9fcec9": "setApproval(address,address,uint256)", "0xe6d95eb8": "DSAuthorized()", "0x34c0d654": "setPackageDb(address)", "0x0ee79fb3": "closeReferendums()", "0xe2cdd42a": "vote(uint256,address,bool)", "0xd3f297d6": "claimLiquidityReward()", "0xf37b437b": "scheduleCall(address,bytes,uint256,uint256,uint8,uint256,uint256)", "0x2090cf8b": "consultBalance(address)", "0xc9296d14": "scheduleTransaction(address,uint256,uint256,uint256,bytes)", "0x7993e5c2": "Devcon2TokenForTesting()", "0x268bb78e": "propose(address,bytes,uint256,uint256)", "0x2b16b56a": "setIndex(uint256,uint256)", "0x6d15f208": "reject(string,uint256,uint16,address,uint256)", "0xc4ff3614": "Wallet(address[],uint256,uint256)", "0xb47d89ad": "Details()", "0x0ae08793": "confirmAndCheck(bytes32)", "0x061e494f": "getBet(uint256)", "0x314e99a2": "abdicate()", "0xe487eb58": "getOwner(bytes20)", "0x7ee65635": "LookAtDepositsToPlay()", "0x9e9d3aa4": "FirstBloodToken(address,address,uint256,uint256)", "0x4dfd1b02": "setUint8(int8,uint8)", "0x82fbdc9c": "register(bytes)", "0xa8b60b93": "ackMsg(uint256,string)", "0x081e806d": "PayOut(uint256)", "0x8bab8791": "testPkgUpdate()", "0xc262df45": "isKnownRequest(address,address)", "0x4123cb6b": "m_numOwners()", "0x62be3172": "Message(address,address,address,string)", "0x0d368fee": "deverify(address)", "0x5f1231ea": "getMemberInfo(address)", "0xa07daa65": "newRequest(uint256)", "0xa4406bcd": "placeSellOrder(uint256,uint256)", "0x5b7d47a9": "betOnColor(bool,bool)", "0x9c6034a7": "sendIfNotForked()", "0x26a4861c": "CROWDFUNDING_PERIOD()", "0xbaac5300": "createTokenProxy(address)", "0xfc72c1ef": "ERC20Base(uint256)", "0x316b08a0": "scheduleTransaction(address,bytes,uint256[7],uint256)", "0x0b590c6b": "SingularDTVToken()", "0x750e443a": "voteAgainst(uint256)", "0xfc7b9c18": "totalDebt()", "0x7ff9b596": "tokenPrice()", "0xd67cbec9": "release(uint32,uint32,uint32,bytes20)", "0x553cc48d": "Player(string)", "0x579cdf17": "getAdminName(address)", "0x7e1c4205": "query2(uint256,string,string,string,uint256)", "0x54107401": "declareLove(string,string)", "0xea4ba8eb": "getOutcome(bytes)", "0x9b5adea2": "setMinter()", "0x185061da": "undoIt()", "0x90c3a370": "AuctionMaster()", "0xbd02e4f6": "calcRandomNumberAndGetPreliminaryGameResult(uint256,uint64)", "0xbc126ba1": "totalCents()", "0xa3747fef": "register(bytes,bytes)", "0x805210b7": "AmIPlayer2()", "0x4e05ded6": "ClassicCheck()", "0xec2ac54e": "deposit(address,uint256,bytes32,uint256)", "0x49c15bd9": "Purchase()", "0x87bb7ae0": "getTicketPrice()", "0xf2f03877": "commit(uint256,bytes32)", "0x167d3e9c": "SetOwner(address)", "0x5c634241": "CanaryV6()", "0xba15e52e": "getInfo(bytes20)", "0x06c1df7b": "checkBetColumn(uint8)", "0xf7bc39bf": "owns(address)", "0x27b752b8": "sha3HexAddress(address)", "0x5ac801fe": "setName(bytes32)", "0x1ae460e5": "isInPool()", "0x85c7a953": "WithdrawFullBalanceFromBankAccount()", "0x755b5b75": "setNumUnits(uint256,uint256)", "0xaefc8c72": "unsealBid(bytes32,address,uint256,bytes32)", "0xfab43cb1": "getPongAddress()", "0x9e997121": "getConfigAddress(bytes)", "0xda2b7416": "testBitsAndFailIndexOOB()", "0xd0e30db0": "deposit()", "0x4a0d89ba": "getSwap(uint256)", "0x63a9c3d7": "verify(address)", "0x337b1cf9": "setIpfsHash(bytes)", "0xbaf00f76": "removeAllSubUsers()", "0x1b370abb": "getPreviousNode(bytes)", "0x741e2345": "registerMany(address,uint256,int256,uint256,bytes20,address,bytes)", "0xb3760c80": "orderMatch(uint256,uint256,uint256,int256,uint256,uint256,address,uint8,bytes,bytes,int256)", "0x73ffd969": "setMap(uint256,uint256,uint256)", "0x50b44712": "tickets(uint256)", "0x6a9d2afd": "playToWinTest(uint256)", "0x644998ae": "maintain(int256,uint256,uint256)", "0x2203ab56": "ABI(bytes32,uint256)", "0x9c67f06f": "registryStarted()", "0x93423e9c": "getAccountBalance(address)", "0x524e4e61": "testDistribution()", "0xa17042cc": "getMsgValue()", "0x4f9d719e": "testEvent()", "0xcbcaacab": "checkTransferWithReference(address,uint256,string)", "0x1bcf5758": "getOccupies(uint8)", "0x1d4b0796": "updateTxStats()", "0xd173707d": "hasPhysicalAddress(address)", "0xa54a2b8b": "testBlockHashFetch()", "0xc51bf934": "CEILING()", "0x0e54b872": "registerUser(string,address)", "0xba51a6df": "changeRequirement(uint256)", "0x6bd92f7c": "activateAllowanceRecord(address,address)", "0x90daaf67": "getMinimalDeposit()", "0x85d5c971": "logTransfer(address,address,bytes32)", "0x8b9e5385": "MeterSlock(uint256,uint256,address)", "0x7bcd7fad": "getRecordAtIndex(uint256)", "0x8e035ac1": "BetOnHashV82()", "0x5dbe47e8": "contains(address)", "0x21bb79fe": "luckyDogInfo()", "0xdd467064": "lock(uint256)", "0xa4beffa7": "increaseInvestment()", "0x8400c307": "isRecipientAllowed(address)", "0x0965bf7d": "processProposals()", "0x4b5dc8cb": "roundMoneyDown3SFExt(uint256)", "0x777feff5": "getCertificationDbAtIndex(uint256)", "0x9462eae5": "ChangeContractor(address)", "0x4c6d1d9e": "checkOutTag(string)", "0x09a399a7": "personAdd(string,int256,int256,string)", "0x31380c89": "TokenSale()", "0xd0bff051": "testSetBalanceDb()", "0x4c738909": "getMyBalance()", "0xb2310cc5": "payRequstedSum(uint256,uint256)", "0xda3c300d": "currentFee()", "0x6ed7c013": "move_monsters()", "0x4f073130": "takeOrder(bool,uint256,uint256)", "0x6860fd58": "Fees(uint256)", "0x214c9d50": "WritedrawStatus()", "0xf314bf46": "setReleaseDb(address)", "0x561a4873": "buyAd(string,string,string,uint256,uint8,address)", "0xf249cf19": "get_all_challenges()", "0xbbed7177": "getContentTimestamp(uint256)", "0xc864e760": "recordCommissionEarned(uint256)", "0x1896f70a": "setResolver(bytes32,address)", "0xfd35e71b": "entryPayoutDue(uint256)", "0x5a58cd4c": "deleteContract()", "0xb29b5366": "setRentable(bool)", "0xad5c613d": "purchase(bytes)", "0x6949a058": "sendOwnerEther()", "0xc03e382f": "calculateShare()", "0xf5bade66": "setDeposit(uint256)", "0x384e5018": "etherandomCallbackAddress()", "0xf06186c7": "testReality()", "0x677342ce": "sqrt(uint256)", "0x10e89b22": "remove_deal(uint32)", "0xf2b445ad": "rowround(uint256,uint256)", "0xd7ed7453": "redeemWinnings(uint256)", "0x92b4bb50": "rps()", "0x089327de": "MyToken()", "0x87ebd76c": "initContract(string,string,uint256,uint256)", "0xdbc45228": "newProposal(address,uint256,bytes,bytes)", "0x6b1feeeb": "get_my_sig()", "0x6837ff1e": "newContract(address)", "0x9f181b5e": "tokenCount()", "0x92ba4ba6": "GridMember(string,uint256,bool,address,address)", "0x45755dd6": "returnFunds(uint256)", "0xb4b9d1f1": "lookup(uint256,uint256)", "0x98024f18": "testThrowsTransferDisableNotEnabled()", "0x9e7b8d61": "giveRightToVote(address)", "0x8112821f": "EthVentures()", "0xe65d6b49": "getCommission()", "0x068c966b": "DrawDetails(uint256)", "0x9bb5239a": "CheckPrize(address,uint256)", "0xff08d2b0": "PayMiners()", "0x9be1fcee": "BankOwner_DisableConnectBankAccountToNewOwnerAddress()", "0x5f972df8": "_jDiv(uint256,uint256,uint256,uint256)", "0xfe8b6642": "setEnforceRevisions(bytes32)", "0xe4cc1161": "seedWithGasLimit(uint256)", "0x5fd4b08a": "getName(address)", "0xaa51793c": "isLosingBet(uint256)", "0x31757f2e": "collisionCount()", "0xe1f5ebc5": "_projectAddNew(address,uint256)", "0x64228857": "getRevisionCount(bytes32)", "0x5ca3400c": "WithBeneficiary(address)", "0x39f4debc": "fillOrderAuto()", "0xcc2c5453": "add_sword(uint16)", "0x0a19b14a": "trade(address,uint256,address,uint256,uint256,uint256,address,uint8,bytes32,bytes32,uint256)", "0x4d207d9a": "identify(address)", "0x3e83fe36": "getMyShares()", "0x8bb0faee": "setRef(string,string)", "0x38bbfa50": "__callback(bytes32,string,bytes)", "0xcee6f93c": "getResultOfLastFlip()", "0xbbba3333": "safer_ecrecover(bytes32,uint8,bytes32,bytes32)", "0x8e19899e": "withdraw(bytes32)", "0xd8389dc5": "hash(bytes32)", "0x8a4068dd": "transfer()", "0xea9e107a": "acceptRegistrarTransfer(bytes32,address,uint256)", "0x8f4ffcb1": "receiveApproval(address,uint256,address,bytes)", "0xf67abd87": "entryDetails(uint256)", "0x67acd805": "lowerMinWager(uint256)", "0xec035393": "_getAllRevisionBlockNumbers(bytes20)", "0xbd858288": "orderMatch(uint256,uint256,int256,uint256,uint256,address,uint8,bytes32,bytes32,int256)", "0x112c7075": "ManualDeposit()", "0xd81a91e9": "get_party2()", "0xc52bd836": "setDappOwner(bytes32,address)", "0xf84f420b": "getRandomNumber(address,uint256)", "0xcfe9a7b8": "getPackageName(uint256)", "0xe97dcb62": "enter()", "0x48db5f89": "player()", "0x6bdbf8e6": "concat()", "0x3c959aca": "CheckTickets()", "0x3aa5f4f7": "changeTokenSettings(uint16,uint256,uint256)", "0xac20902e": "NormalizeMoney()", "0x2fac1d36": "isReadyFor(address)", "0xdcaa5620": "findNextWeekday(uint256,bytes)", "0xf9909915": "bulkStoreHeader(bytes,int256,bytes,int256)", "0xcd2cdd5b": "claimOwnershi()", "0xcfae3217": "greet()", "0xf5c8d71b": "forceMove(address,address,uint256)", "0x9718b524": "newTreasury(address)", "0xd0679d34": "send(address,uint256)", "0x1301ee02": "transferringETC(address)", "0x60eb2826": "Badge()", "0x0d0c2008": "TwoAndAHalfPonzi()", "0x17e1bfb7": "addInstitution(address,string)", "0x06394c9b": "changeOperator(address)", "0x80c951bf": "currentClaimPriceInFinney()", "0xd063f55f": "toLittleEndian(uint64)", "0x53f818d6": "checkBetValue()", "0x9205fbc2": "testAuthorityAuth()", "0x3e4c0c82": "player_1(uint256)", "0xe571c35e": "ReverseRegistrar(address,bytes32)", "0x24804cef": "Deed()", "0x622e88cb": "testBitsXorSuccess()", "0xdfca2f53": "LookAtPrizes()", "0xafa293d4": "getSource()", "0x755f99c2": "AddNewSmallContract(address)", "0x7137ed47": "setProxyContract(address)", "0x835b42fc": "testThrowUpdateLatestRevisionNotUpdatable()", "0xdd34e129": "PriceTest()", "0xedb27f4e": "switchWizard(address)", "0x1c5d9faa": "setNickname(string)", "0x4746cef8": "_confirmAndCheck(address,bytes32)", "0x189c94ae": "testFallbackStaticSig()", "0x0cb749b6": "FutureBlockCall(address,uint256,uint8,address,bytes,bytes,uint256,uint256,uint16,uint256,uint256)", "0x2b25a7e4": "giveKudos(address,uint256)", "0x294f3d4d": "setUpLimit(uint256)", "0x2cce81aa": "getBlockHash(int256)", "0x4cd11943": "NewManualInvestor(address,uint256)", "0x7eaef50c": "over()", "0x8ac4e1d8": "TemperatureOracle()", "0xf108a7d2": "withdraw(uint256,address,string)", "0x00a676f9": "getExists(bytes32)", "0xb8d4efb5": "validate_percent(uint8)", "0xc7489441": "closeMarketMaker(uint256)", "0x3def449b": "FipsNotary()", "0x5687f2b8": "emitApproval(address,address,uint256)", "0xa9f8ec6c": "AlarmClockTipFaucet()", "0xd8e5c048": "scheduleCall(address,uint256,uint256)", "0x135217e7": "requires_depth()", "0x0aa46c12": "testClearBitFailIndexOOB()", "0x77d32e94": "ecrecovery(bytes32,bytes)", "0xace523c4": "createReferendum(string,string,uint256,uint256)", "0x5ca8bc52": "returnIt()", "0xdb318833": "_ecAdd(uint256,uint256,uint256,uint256,uint256,uint256)", "0x623195b0": "setABI(bytes32,uint256,bytes)", "0xd7bb99ba": "contribute()", "0x2880ebe7": "underdogPayoutMarkup()", "0x4ce01d86": "totalBetValue()", "0x837a7ba5": "testThrowTransferDisabled()", "0x386fcda8": "testCreateCostToken()", "0x0e850239": "scheduleCall(bytes4,bytes)", "0x163aba3c": "getQueryFee()", "0x9941e3d0": "setCallAddress(address)", "0x23637e60": "votePrice(uint256,bool)", "0xde78e78a": "tokenLaunched()", "0xe3579ea5": "publish(string,string,address,uint256)", "0x59a547b0": "recordCommission(uint256)", "0x1aa86370": "updateXIPFSPublicKey(string)", "0x97fcb54e": "transfer_eth(address,uint256)", "0x05d2f92a": "check_depth(address,uint256)", "0xdfcbb794": "TrustFund(address,uint256,address)", "0xb7dd1d17": "getAllRevisionBlockNumbers(bytes32)", "0x75862df4": "TokenWithEStop(address)", "0xd22057a9": "register(bytes32,address)", "0x29d017b5": "TestWithConstructor(address,uint256[])", "0xd216d55d": "etherandomExec(bytes32,bytes32,uint256)", "0xfba06849": "fipsPublishDataMulti(bytes20[],bytes)", "0xa37fd390": "setHomeAdv(uint256,string)", "0xcf2e3efc": "GetBankAccountBalance()", "0x423e7e79": "_dispatchEarnings()", "0x74087040": "testBitsNotEqualSuccess()", "0x61d585da": "state(bytes32)", "0xcfb3a493": "getMyBounty(uint256)", "0x5afeb106": "Sqrt()", "0xf9e84395": "unexempt(address)", "0x5669c94f": "issueToken(address,string)", "0x19b05f49": "accept(uint256)", "0x3ae01f84": "USDOracle()", "0x8c172fa2": "getEvent(bytes32)", "0x4671e65e": "proposeEmergencyWithdrawal(address)", "0xc27d7721": "create(uint256[101][])", "0x5c52e51e": "processPayout()", "0xf7a0fa0a": "getShareDistribution(bytes)", "0x31a3a506": "closeFunding()", "0x465e759b": "testRestart()", "0xb60d4288": "fund()", "0x52200a13": "getNumHolders(uint256)", "0xf2c298be": "register(string)", "0x7bc25372": "UserCheckBalance(address)", "0x104d5fdd": "getPriceProxy()", "0x447cd682": "scheduleTransaction(address,uint256)", "0xa045fdff": "scheduleCall(address,bytes)", "0x4757f1d2": "redeemAllOutcomes(uint256,uint256)", "0x5e855f14": "Dice(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)", "0x5d3c1d4c": "_getRequest(uint256)", "0x416c8701": "beyond()", "0x63aea3e0": "PlayerInfo(uint256)", "0xa163a624": "Test()", "0xedede601": "testBalance()", "0x13651124": "WithdrawAmountFromBankAccount(uint256)", "0x893d20e8": "getOwner()", "0x90b5561d": "insert(uint256)", "0xf9983a12": "GetMyInvestmentBalance()", "0xb71c47a2": "surrender()", "0xf2a75fe4": "empty()", "0x804ba97a": "tryGet(bytes)", "0x6506b623": "rotateBitsLeft(bytes,uint256)", "0x3ef8ec78": "announce_numbers(uint8,uint8,uint8,uint8,uint32,bytes32)", "0x73abecbb": "kill1()", "0xd5171523": "euroteambet()", "0x8e52cb51": "getRecordKey(bytes,bytes,bytes)", "0x7adbf973": "setOracle(address)", "0x4aa16737": "enter(uint8)", "0xf0cb556c": "updateLatestRevision(bytes32,bytes)", "0xbc8fbbf8": "nuke()", "0xc8e55708": "oraclize_query(string,string[1])", "0x7332b520": "getRewardsCount(uint256)", "0xf7c2b38c": "seconds_left()", "0xba344743": "_rawTransfer(address,address,uint256)", "0xcab5c0f1": "_incrementState()", "0xe044c2de": "newLoan(bytes,address,uint256,uint256,uint256,uint256,uint256,uint256)", "0x76abc03b": "getShareDistribution(uint256)", "0xf0da84f8": "getTransferable(bytes32)", "0xcde99727": "calculateROI()", "0x155dd5ee": "withdrawFunds(uint256)", "0x8b543b80": "maximumCredit(address)", "0x340ddda6": "MeatConversionCalculator(uint256,uint256)", "0x524f3889": "getPrice(string)", "0x84054d3d": "cashout()", "0x856f3080": "WhatWasMyHash(bytes32)", "0x0386a016": "closeProposal(uint256)", "0xcebce72d": "token(uint64)", "0x7f480f9d": "processDividends(address)", "0x11d12402": "testEasyPropose()", "0x2f695053": "getCertifierAtIndex(uint256)", "0xd9fcb31f": "comm_channel()", "0x141c4e60": "challenge(uint256,address)", "0x4ff13571": "x2()", "0xa01bc729": "monster_attack(uint256)", "0x2fe9541f": "addIssueBounty(string,uint256)", "0x5503a659": "smallponzi()", "0xdfc765dc": "getMatchers_by_index(uint256)", "0x0b7623ba": "abs(int8)", "0xcde0a4f8": "setRegulator(address)", "0xf95b5a58": "getInitialAnswer(uint256)", "0x66b42dcb": "register(address,string,uint256,string)", "0x9f2ce678": "vote(bytes32,bool)", "0xb3559460": "getGenerationSize(uint256)", "0x5ddae283": "transferRegistrars(bytes32)", "0x59dc735c": "getClient()", "0xc258ff74": "List()", "0x4fb4bcec": "step5()", "0xed684cc6": "trigger(uint256)", "0x09405164": "getOpenCandidates()", "0x5c5d625e": "getProof()", "0x9f5f7c7f": "tokenSplit(address,address,address,uint256)", "0x0e38901a": "unvault(uint256)", "0x75160a20": "pay_royalties()", "0x15398afe": "compareNumericStrings(string,string)", "0xbbd8b602": "getOracleOutcomes(bytes,address[])", "0xebae35a6": "DAOTokenCreationProxyTransferer(address,address)", "0x15abc160": "createValidatedRequest(address[3],address,uint256[11],uint256,bytes)", "0x830953ab": "claimAmount()", "0x26b916b4": "Set_Interest_Rate(uint256)", "0x1fb291cb": "registerInt(address,int256)", "0x505fb46c": "add(uint256,uint256,uint256)", "0xf00d4b5d": "changeOwner(address,address)", "0x034187fd": "setEthToCents(uint256)", "0x94d9cf8f": "CreateProxyWithControllerAndRecovery(address,address[],uint256,uint256)", "0xacbf98a7": "endsWith()", "0xfc2c3e08": "getIteration()", "0x6d7da0b1": "MyContract()", "0x1558ae4d": "Etheroll()", "0x42cbb15c": "getBlockNumber()", "0x29cd62ea": "setPubkey(bytes32,bytes32,bytes32)", "0x2030f721": "num_objects()", "0xbc08afd9": "WebOfTrustToken(address,uint256)", "0x8cdfb1e6": "transferIfHF(address)", "0xa0bd3c0f": "scheduleCall(address,bytes,bytes,uint256)", "0x4e71e0c8": "claimOwnership()", "0xc1cc0775": "calculateFeeDynamic(uint256,uint256)", "0x50c42921": "replicate()", "0x25495998": "getMinimumConsumerDeposit()", "0x3d8e2947": "getFileAddress(bytes)", "0x1f794436": "getBlockHeader(int256)", "0x7d380265": "addOptionChain(uint256,string,uint256,uint256,bytes32,address,int256[])", "0xec0b4153": "getMoneyness(int256,uint256,uint256)", "0x01775f23": "_closeBooks()", "0x9d063ed8": "FIFSRegistrar(address,bytes32)", "0x083b2732": "callback()", "0xa1920586": "offer(uint256,uint256)", "0x19c47214": "getBlockVersion(bytes)", "0xa293d1e8": "safeSub(uint256,uint256)", "0xfe73e3ec": "preliminaryGameResult(uint64)", "0xf004b12b": "CrowdFund(uint256,uint256,address)", "0x54d03b5c": "changeFeeMake(uint256)", "0x9dbc4f9b": "participantDetails(uint256)", "0xd002462b": "setDeploymentFee(uint256)", "0xed2b8e0b": "getPoolRotationDelay()", "0xf697a0ed": "ppb(uint256,uint256)", "0x964c836c": "receiveExecutionNotification()", "0x5e0e2957": "dumpOut()", "0x33232609": "blake2b(uint64[],uint64[],uint64)", "0x88f53db1": "getDataRequest(uint256)", "0x0caf9d39": "testFailTooManyMembers()", "0x1f2e886c": "testControllerTransferTriggersEvent()", "0x586a69fa": "getMaximumStackCheck()", "0xf64fca2e": "getNodeId(bytes)", "0x8b95ec0c": "testAddBalance()", "0x32e7c5bf": "B()", "0x57e6c2f4": "isAuthorized()", "0xb2f2588b": "sortNumbers(uint8[3])", "0xe95bee59": "checkFormat(string)", "0xcef887b0": "storeBlockWithFee(bytes,int256)", "0xbe26733c": "Kill()", "0xe82f7dd4": "testThrowsRetractLatestRevisionNotUpdatable()", "0xfd7ac203": "TestToken()", "0x6d052f56": "testBitsSetSuccess()", "0xf65c4d42": "Participate(uint256)", "0x432c685f": "trustClient(address)", "0xb0171fa4": "getCurrentGenerationId()", "0x03251a08": "setMin(uint256,uint256)", "0x58d3b617": "Notifier(string)", "0x15dacbea": "transferFrom(address,address,address,uint256)", "0x83f7b8e1": "getNumberOfPhotos()", "0xf1076703": "getVerificationId(address,bytes,bytes)", "0x752a3df6": "transferIfHardForked(address)", "0xaf93afdd": "Shipment(bytes,bytes,bytes,bytes,string,bytes,uint256,uint256,bytes,bytes,uint256,uint256,string,bytes,bytes,bytes)", "0x7fcf532c": "Withdrawal(address,uint256)", "0x72ea4b8c": "getNumInvestors()", "0x7df52ba8": "Arbitrate(uint32,uint32,bool)", "0xea25f24a": "TokenCreation(uint256,uint256,address)", "0xf74100e3": "getBits(bytes)", "0x63bfe3d8": "SkillBeatsLuck()", "0x80599e4b": "remove(string)", "0x6c050eae": "look()", "0xf9a794ad": "EtherLovers()", "0x501e8428": "getPart(bytes,uint256)", "0xf446c1d0": "A()", "0x2d67bb91": "World()", "0xeef547d7": "deal_details(uint32)", "0xa6cb9e64": "scheduleCall(address,bytes,bytes)", "0x659010e7": "m_spentToday()", "0x9b0b9c07": "acceptBankDraft()", "0x315e2f1b": "setTestString(string)", "0x69bcdb7d": "getCommitment(uint256)", "0xe1376da2": "updateFirstActiveGamble(uint256)", "0xc70d169d": "answerRequest(uint256,bytes)", "0xa094a031": "isReady()", "0x74e60a48": "cancelOrder(address,uint256,address,uint256,uint256,uint256,address,uint8,bytes32,bytes32)", "0x1b5ee6ae": "mintToken(int256,address,uint256)", "0x7f791833": "toTimestamp(uint16,uint8,uint8,uint8)", "0x5e6ad49d": "_setCosignerAddress(address)", "0xb2e85b67": "getPlayerStatus(address,uint256)", "0x30a24abd": "create(bytes4,bytes)", "0x9f0e3107": "get_timestamp(bytes32)", "0x33397816": "withdrawAccountBalance(address)", "0x0da3e613": "EthFactory()", "0xa5f3c23b": "add(int256,int256)", "0xc1829a14": "testFailTooFewConfirms()", "0x5322f0c5": "getChannelOwner(bytes)", "0x0fd1f94e": "firstClaimBlock()", "0x639d57f2": "testGetBitSuccess()", "0xd3aa22c7": "transferTLA(string,address)", "0x86314af9": "BetOnHashV84()", "0x3059ac30": "Escrow(address,address)", "0x0efafd01": "getPlayerGainLossOnLastFlip()", "0x49bf66d3": "addRegistryIntoNameIndex(address)", "0x3af39c21": "undefined()", "0xb660d77c": "switchMPO(address,address)", "0x0fdb468f": "fee(uint64)", "0x72479140": "CreateTicket(address,uint8,uint8,uint8)", "0xe79a198f": "unregister()", "0x688dcfd7": "setProofType(bytes1)", "0xfb9a4595": "GitHubBounty()", "0xd02a9889": "getDateOfFirstPayment()", "0xca35271c": "numDebtors(address)", "0x08714bfa": "TestContract()", "0x16d960b5": "createThing(bytes32[],bytes32[],uint88)", "0x17961d0f": "ord()", "0x62ba9687": "toTimestamp(uint16,uint8,uint8,uint8,uint8)", "0xbba91ea7": "getHomeadvIndex(uint256)", "0xb45105b2": "post(string,address,string)", "0xa7e25683": "testShortOutput()", "0xa068e8d3": "convict(uint256,uint256,uint256,uint256)", "0x09a69f57": "getRewardAmount()", "0xe7334156": "processNextDeposit(address)", "0x62ee6d29": "changeHashtoLowOrHigh(uint256)", "0xa80d4e9a": "EtherAuction(uint256)", "0x18489f50": "thingExist(bytes32[])", "0x5323c6cf": "calcCostsBuying(bytes,uint256,uint256[],uint8,uint256)", "0x9a777d5d": "buyCoins()", "0x36344022": "testAuthorizedTransfer()", "0x8b863095": "setContractorProposal(uint256,bytes)", "0xd10e99fe": "mint(int256,bytes32)", "0x12c82bcc": "sendRobust(address,uint256)", "0x0bf75567": "voteSuperQuorum(uint256,bool)", "0xf5f6ea26": "EthOne()", "0x14918f5e": "performInitialWithdrawal()", "0xdf32754b": "owned()", "0x1632070c": "setRewardDivisor(uint256)", "0xe50a3bb1": "oraclize_query(string,string[],uint256)", "0x9b619d3b": "_deleteAllPackedRevisionBlockNumbers(bytes32)", "0x3b751f7f": "claimThroneRP(string)", "0xd55ec697": "upgrade()", "0x43703b0e": "getEventData(bytes)", "0xd1bf9aba": "nextRune()", "0x76849376": "addNode(bytes32,address)", "0xfd958695": "isAlphaNumeric(bytes1)", "0x9fa5e5d5": "setARKowner(address)", "0x6e2cde85": "drawPot(string,string)", "0x4d5b080c": "scheduleTransaction(uint256,address,uint256)", "0xf1e4a540": "unsetCoordinator()", "0x364f4896": "emission(address,address,uint256,uint16,uint16)", "0x9183fd01": "getSeedPrice()", "0x9832ee65": "resultsWeightedByTokens()", "0xdd5244b4": "testTryProxyCallWithValue()", "0xec2ec781": "testFailGetUnsetToken()", "0x7db9743b": "Registry()", "0x1adf2d1a": "Offer(address,address,bytes,uint256,uint256,uint128,uint256)", "0x1ba326c4": "calcShare(uint256,uint256,uint256)", "0x3ced842b": "make_offer()", "0xea295ec2": "calcRevenue(address)", "0x3c894475": "scheduleTransaction(address,bytes,uint8,uint256[6],uint256)", "0x477bddaa": "setContractAddress(address)", "0xed180443": "getUint256(int256)", "0xee725d44": "toChannelID(string)", "0xa4898fd5": "deployContract(address)", "0x75ee85bd": "salsa20_8(uint256,uint256)", "0xbe7c29c1": "getNewDAOAddress(uint256)", "0xfe01f1ff": "TokenTester()", "0x6103d915": "Winners(uint256)", "0xa30b5c69": "AttributeModel()", "0x81064e2d": "getCreditorAmounts()", "0x23584a21": "initStats(string,address,uint256)", "0xe2894a8a": "OwnerAnnounce(string)", "0xa84c5330": "createNewRevision(bytes20,bytes)", "0xb742398b": "trade(address,uint256,bytes,address,uint256,bytes)", "0x3211bb90": "OwnerAddFunds()", "0xa0befa94": "getStake(uint256,uint256)", "0x6dc3edcf": "executeExecutable(uint256,uint256)", "0x17e875e3": "Transparancy()", "0x6939864b": "lotteryState()", "0x3b107682": "DualIndex()", "0x51b3d7b9": "_transferWithReference(address,uint256,string)", "0xdf7cec28": "cancelBid(bytes32)", "0x1e74a2d3": "getMinimumEndowment()", "0x39b333d9": "Play(uint8,uint8,uint8,uint8)", "0xcbd08c8c": "config(uint256,uint256,uint256,uint256)", "0xca6ad1e4": "setCustomGasPrice(uint256)", "0x510f44cb": "TestFactoryUser()", "0xcee6ee38": "aEthereumlotteryNet()", "0x11610c25": "bet()", "0xb73405a9": "roundMoneyDownNicely(uint256)", "0xb8851fea": "endDateStart()", "0xa4325485": "getCreatorBalance()", "0x2c181929": "getChainWork()", "0xffb4c857": "_confirmAndCheck(bytes32)", "0x9e66cd38": "free(uint64)", "0x44e43cb8": "depositRevenue()", "0xa553a597": "configure(uint256,uint256,uint8,address)", "0xc47f0027": "setName(string)", "0x565a2ecf": "classicTransfer(address)", "0x1da0fb1b": "updateSettings(uint256,uint256,uint256,uint256,uint256,bool)", "0xa5ebf389": "getMoneyTotals()", "0x7f445c24": "subRegistrar(string)", "0x9ad4f98e": "BlocksureInfo()", "0xd6b4ec12": "getDailyWithdrawalLimit()", "0xe0886f90": "at(uint256)", "0x5dc77e26": "andThen(string,address)", "0xe7d50e5c": "FarmShare()", "0x3f2965f0": "registerSeller(address)", "0x85b73d3c": "testCreateNewRevision()", "0x49437210": "getUpdatable(bytes32)", "0xe1a9109d": "setSeedPrice(uint256)", "0x95978868": "strConcat(string,string,string,string,string)", "0x511b1df9": "addr(string)", "0xc5b1d9aa": "newRound()", "0xecf6eb22": "setConfigAddress(bytes,address)", "0x9a9c9c53": "DepositToBankAccount()", "0x27ea6f2b": "setLimit(uint256)", "0xd2dc0869": "add(string,uint256,string,string,address)", "0xc86a90fe": "sendCoin(uint256,address)", "0x5dfc2e4a": "noop()", "0xd81e8423": "get(address,address)", "0x4cad42d3": "testWager()", "0xd120a284": "getBytesFromNumbers(uint8[3])", "0x991ffd4e": "scheduleCall(address,bytes,bytes,uint256,uint256,uint8,uint256)", "0x60c311fd": "doBurnFromContract(address,uint256)", "0xbb6a0853": "GreedPit()", "0xc27b2c2d": "collectEarnings()", "0x446294ad": "multiAccessGetOwners()", "0x5bec9e67": "infinite()", "0x47e7ef24": "deposit(address,uint256)", "0x8d216186": "roll(uint256,bytes32)", "0xdf300b46": "getThing(bytes32[])", "0x5a6c787e": "updateWithMPO()", "0x1f2dc5ef": "divisor()", "0x421aeda6": "Set_your_game_number(string)", "0xba1162d7": "getFmLength()", "0x853255cc": "sum()", "0x20768ee8": "getProposalID()", "0xf5c57382": "nameOf(address)", "0x4e417a98": "callData()", "0xc90d080a": "registerEvent(bytes)", "0x0b1e400a": "_transferFromToICAPWithReference(address,bytes32,uint256,string)", "0xe420264a": "g(uint256)", "0x8a00a82f": "withdrawRewardFor(address)", "0x06638e92": "GetNumbersFromHash(bytes32)", "0xf63da25b": "Emailer()", "0xc01706dd": "getContentByRank(address,uint256,uint256)", "0xe1108706": "rfind()", "0xffd10e07": "enterPool(address)", "0xc4254c7b": "CoreWallet()", "0x30e0789e": "_transfer(address,address,uint256)", "0x992ae976": "isSafePunctuation(bytes1)", "0x4afce471": "test_requires_depth(uint16)", "0xda7fc24f": "setBackend(address)", "0xeb7402f5": "multiAccessHasConfirmed(bytes32,address)", "0x6c1a5b8c": "TOKEN_TARGET()", "0xb3a2a999": "nextWithdrawal(bytes16)", "0x4a420138": "scheduleHeartbeat()", "0xb1233451": "setTerm(uint256,string)", "0x266710ca": "manualUpdateBalances_only_Dev()", "0x98866c1a": "personUpdateDOD(uint256,int256)", "0xaa6be303": "debtors(address)", "0xda5c0a7c": "testDisown()", "0x8757a2cd": "test_depth(uint256,uint256)", "0xe1bedf2a": "AlarmTester(address)", "0x5dcbac7a": "registerBytes(address,bytes)", "0xa587da29": "setPackage(bytes,uint8,uint8,uint8,bytes)", "0xc3da42b8": "c()", "0x4b8772c1": "buyUnit(uint256,uint256)", "0x67f12ecf": "validate(address,uint256,uint256[101][])", "0x0bd089ab": "MyAdvancedToken(uint256,string,uint8,string,address)", "0x32829a23": "OpenBankAccount()", "0xdea06188": "NumberOfBlockAlreadyMined()", "0x61e539da": "testFailWrongAccountTransfers()", "0x8a3e44d4": "assetMoveInformation(address,address)", "0x1327d3d8": "setValidator(address)", "0x207c64fb": "validate(address)", "0x80acaafb": "profitDistribution()", "0x90b98a11": "sendCoin(address,uint256)", "0x8b147245": "update(bytes32)", "0x920c94df": "BuyTicketForOther(address,uint8,uint8,uint8)", "0x6f698fb5": "setMinimumQuorum(uint256)", "0xac5e81a9": "historyPayout(address)", "0x70d084c0": "SingularDTVCrowdfunding()", "0x67ce940d": "getOverhead()", "0x7a791524": "setNextFeePercentage(uint8)", "0xd6d902c4": "claimThroneFor(bytes,address)", "0xc71e48d6": "setOutcome(bytes32,bytes32[])", "0xef41e06f": "testThrowSetEnforceRevisionsNotOwner()", "0x70be4ffa": "testErrorUnauthorizedSetPackage()", "0x6d12301c": "getBetValue(bytes32,uint8)", "0x4e69d560": "getStatus()", "0x55234ec0": "remaining()", "0x5cfd8c24": "ResetPonzi()", "0xe29fb547": "scheduleCall(bytes4,uint256,uint256,uint8,uint256)", "0xbd119967": "add_rating(uint256,uint256)", "0x966acb38": "testThrowTransferNotTransferable()", "0x5c665f89": "getFunds(address,bool)", "0xa59d6986": "recoverLostFunds()", "0x403abbc7": "updateFirstActiveGamble()", "0x0230a07c": "releaseDeed(bytes32)", "0x2d116186": "deityBalance()", "0x602acca1": "InchainICO(address[],uint256)", "0xd393c871": "register(string,address,uint256)", "0xe0fe075e": "payoutReady()", "0xf3e3c629": "testBalanceOfStartsAtZero()", "0x48cd4cb1": "startBlock()", "0x669459a7": "removeRegistryFromOwnerIndex(address)", "0x74d89c47": "testUpdateNameDb()", "0x182db370": "getWhatHappened()", "0xf363441f": "getCreatorDotBalance()", "0x6896fabf": "getAccountBalance()", "0xb29a0308": "logAnonymous(bytes,bytes,bytes,uint256)", "0x3023d0c4": "Ethstick()", "0x1077f06c": "makeClaim(uint256)", "0x8a519fb9": "BlockChainEnterprise()", "0xbffbe61c": "node(address)", "0xe20bbd8d": "RecoveryWithTenant()", "0x03bda14e": "raiseMaxNumBets(uint256)", "0xe1041d86": "__throw()", "0x3d79d1c8": "bal()", "0xbd3f0965": "AiraEtherFunds(string,string)", "0xc388cca6": "testBitAndFailIndexOOB()", "0x921f98bb": "resolveFailVote()", "0x4bbb216c": "_target(address)", "0x10922cc1": "testTransferCost()", "0xeceb2945": "checkProposalCode(uint256,address,uint256,bytes)", "0x3df4ddf4": "first()", "0x21a49ec2": "LCoin()", "0x6d1f00a6": "ThroneMaker(uint256)", "0xcaed4f9f": "DataService()", "0x5cff876b": "carrotsCaught()", "0x3a7fb796": "mintGreen(int256,address,uint256)", "0x370b6939": "AdminSetDrawer(address)", "0x2d9a37d3": "getMaxPayout()", "0x739f888c": "setNewEstimate(int256,int256)", "0x04dd69fa": "getGenerationIdForCall(address)", "0xb764e273": "failSend()", "0xd4dfadbf": "getMarket(address)", "0xa0e67e2b": "getOwners()", "0x2d592a34": "sellKissBTC(uint256)", "0x8a341c83": "testErrorRootAuthorityChangeUnownedPackage()", "0xfcc6b5d5": "fillTheirOrder(address)", "0x61649472": "getPoolFreezePeriod()", "0x3dd297da": "safeMultiply(uint256,uint256)", "0x5d3278f0": "LooneyFifty()", "0x7399646a": "theRun()", "0x3e450fff": "adminDeleteAccount()", "0x93cc9162": "taskRejected(uint256,uint256)", "0xbc5d0f65": "beginExecution()", "0x1934d55a": "isPermanentlyApproved(address,address)", "0xf1cff4b5": "testBitsNotSetSuccess()", "0xf240f7c3": "dispute()", "0xf6d5959b": "getActionStatus(uint256)", "0x0f23cbaa": "recycle()", "0x74f8d96e": "getRevisionBlockNumber(bytes20,uint256)", "0x0bad342a": "EscrowContract(address,address,address,address,uint256,uint256,uint256,uint256)", "0xa6afd5fd": "getBets()", "0x84ad6ff3": "ReversibleDemo()", "0x62b3b833": "createCoupon(string)", "0x523ccfa8": "isKnownCall(address)", "0xacc8cb18": "pushTerm(string)", "0xa753d6f2": "CreateProposal(string,string,string,string,string,string,uint32,uint32)", "0xc5699d68": "_compare(int256,bytes,int256)", "0xe2faf044": "createDAO(address,uint256,uint256,uint256)", "0xa15afb48": "Replicator()", "0x352d2790": "UUID4()", "0x7dee2cad": "CancelMyInvestment()", "0xbeabacc8": "transfer(address,address,uint256)", "0x674f220f": "previousOwner()", "0x4b3b6168": "SetNewBigContract(address)", "0x54ba7daa": "enter(bytes,bytes)", "0x06fdde03": "name()", "0x5944427b": "getRequestResult(uint256)", "0x983ef725": "getDifficulty(uint256)", "0x9287c877": "getNavLength()", "0xa987d654": "restoreItem(uint256)", "0x05433a26": "GetNumbersFromHash(bytes)", "0xd04bfc9c": "buyer_pay()", "0x4a9b3f95": "personUpdateName(uint256,string)", "0xe6b972f5": "userName(address)", "0x88782386": "UnicornMilk()", "0x74580e2f": "changeCreator(address)", "0x1785f53c": "removeAdmin(address)", "0xad544dcb": "testSetNotUpdatable()", "0xafd09bab": "quadrupler()", "0x77bc222c": "_eraseSingleNode(bytes32)", "0x09957e69": "newSale(bytes,uint256,uint256)", "0xa21931ea": "CreateProposal(string,string,string,uint32,string,string,string,uint32,uint32)", "0xbb6b4619": "SendETC(address)", "0x3aa94b1d": "getCoinStats(uint256)", "0x9e2262f5": "testCreateCostData()", "0x2bf4e53d": "getCurrentShareholders()", "0x6111dd02": "calcCostsSelling(uint256,uint8,uint8,uint256)", "0x6b9b1006": "TransactionRecorder()", "0x83b23b40": "cEthereumlotteryNet()", "0x770c6cbb": "WithDrawPreForkChildDAO()", "0x67080f6e": "testThrowsSetEnforceRevisionsNotOwner()", "0xa10bee85": "_transferFromWithReference(address,address,uint256,string)", "0x49cc954b": "twoYearsPassed()", "0x88c3ba85": "ParallelGambling()", "0x03985426": "getMode(bytes32)", "0xad8ed335": "__proxy(address)", "0x306387a4": "dealStatus(uint256)", "0x0343dfa0": "checkInvariants()", "0x23df9df5": "_refund(uint256)", "0x837e7cc6": "rollDice()", "0x98b1e06a": "deposit(bytes)", "0xa0440426": "purchaseProduct(uint256,uint256)", "0x4cb71b9b": "getAllReleaseHashes()", "0x3a7d280c": "login(string)", "0xb9a904f9": "testUnauthorizedSetBetaPackage()", "0xe94a4db1": "isSuitableGen(uint256,uint256)", "0x60f8af90": "refundRound()", "0x43bf718e": "getHashOfTheProposalDocument()", "0x4a30f976": "censorship(uint256,bool,bool)", "0x47e46806": "toString()", "0x8d59cc02": "register(address,string,string)", "0xb3fb14ad": "getGameResult()", "0x4a23dc52": "FileStore()", "0x8da5cb5b": "owner()", "0x3cc8daf7": "setNameOwner(bytes,address)", "0x14cbdb54": "EspCoin()", "0xc47cf5de": "getAddress(bytes)", "0x71e11354": "updateRegistration(string,string)", "0x8b2e6dcf": "publish(bytes32)", "0x12d00c2e": "soloWithdraw(uint256)", "0xd68199dc": "gameStats()", "0xf0f967e8": "canCall(address,address,bytes)", "0xe0c7c117": "Randao()", "0xddeae033": "claimFor(address)", "0xcec7260b": "move_monster(uint16,uint16)", "0xe51ace16": "record(string)", "0x4f76cb02": "testGetBitFailIndexOOB()", "0x942385eb": "getPayroll()", "0x46d667db": "setBytes32(bytes)", "0x35a063b4": "abort()", "0xb1d51d31": "pay(uint64,address)", "0x7140bdf3": "get_all_best_offers()", "0x0380e2f3": "getHashOfTheSignedDocument()", "0x2feda2fa": "POI()", "0x3c0870ae": "challenge(uint256,uint256,uint256,bool)", "0x27a5c7c6": "voteDecline(uint256)", "0xafd8c8c4": "GasProxy(address,address)", "0x8de93222": "purchase(address,uint256)", "0x087e055a": "getConfigBool(bytes)", "0xbaccc92b": "RegulatorIfc(address)", "0x8c546f81": "GNT()", "0x57cb2fc4": "getInt8()", "0xe3083fb5": "removeFromContribution(uint256)", "0x1a092541": "getDescription()", "0x7486a8e3": "get_publisher(bytes32)", "0x089e0ad0": "buildDSMap()", "0x29161820": "Base(uint256)", "0xe2f8feb2": "internal_tester(int256)", "0x1d2dbb22": "CancelMyInvest()", "0x726ab4ef": "getParentHash(bytes)", "0x83d8a90f": "theDonkeyKing()", "0x9babdad6": "removeShareholder(address)", "0xdeb931a2": "getOwner(bytes32)", "0x90cb04e1": "buy(string,uint256,uint16)", "0x2ff92323": "oraclize_query(uint256,string,string[4])", "0xf91a792e": "decryptHand(string,uint256,uint256,uint256)", "0xebf6e91d": "hit(uint256)", "0xb085b9a5": "Example()", "0x07b2779f": "BasicRegulator(address,uint256,uint256)", "0xe10e5dce": "_build(bytes)", "0x3e239e1a": "getHour(uint256)", "0xacc5a0dc": "GetPrize()", "0xa79deb4f": "acceptTradeDeal()", "0xd7c26adb": "oraclize_setProof(bytes1)", "0xc6a17d2b": "pow10(uint256,uint8)", "0xa87d942c": "getCount()", "0xe706918c": "testToggleBitSuccess()", "0xc1812b15": "reorganizeOwners()", "0x7c7c7695": "getAccountID(address)", "0x1a26ed1c": "validateReservedWindowSize(uint256,uint256)", "0x87393bc6": "verifyFirstHalf(uint256[4],uint256[4])", "0x2e52d606": "n()", "0x2037fcbf": "withdrawInvestment(uint256)", "0x77228659": "query2(uint256,string,string,string)", "0xf67a1d37": "BlockChainChallenge()", "0xc67146a5": "check_bet(uint256,address,uint8)", "0xc89f2ce4": "funds()", "0x58e29e17": "initiateProof()", "0xf0e10c0d": "play(address,uint256)", "0x480b70bd": "scheduleCall(address,bytes4,uint256,uint256)", "0x5294157f": "sendWithAllOurGasExceptExt(address,uint256,uint256)", "0xad447a19": "getBalanceDB()", "0x41095b60": "voteForUltimateOutcome(bytes,uint16)", "0x5521d17b": "betOnColor(bool)", "0xc8f8d75d": "Config(uint8,address)", "0x10ae4ce2": "setReleaseValidator(address)", "0x9fd4f7d1": "replaceWizard(address)", "0x77c78df9": "getCurrentLevel()", "0x1dda5c7d": "testFailSubBalanceBelowZero()", "0x21e5383a": "addBalance(address,uint256)", "0xb0414a2d": "setMinimumGasLimit(uint256)", "0x919840ad": "check()", "0xf651bf44": "move_to(uint16)", "0x0b97bc86": "startDate()", "0x29f1bff4": "withdrawFromChildDAO(uint256)", "0x7a02dc06": "getInfo(bytes32)", "0xe5bf93b9": "balanceEther(uint256)", "0x288c6ed2": "getSeedCost(uint256)", "0x4db3da83": "scheduleCall(bytes4)", "0x270cfee1": "getTokenAccount()", "0xb5d03751": "YoutubeViews()", "0x27e056a5": "addMinter(int256,address)", "0xfa7d68f1": "getAccountInfo(uint256,uint256)", "0x09fc8f6d": "isTokenUpgraded(bytes32)", "0xa5b9e922": "getContentTimetamp(uint256)", "0x1f8947c1": "extractUint(int256,bytes,uint256,uint256)", "0xbcc941b6": "totalWinners()", "0x1db71ffb": "doLoops(uint256)", "0x35ae41c9": "godAutomaticCollectFee()", "0x42cf0e72": "searchByOwner(address)", "0x69f18967": "testSetBitFailIndexOOB()", "0x57b07cd9": "getReleaseHash(uint256)", "0x2baf4f22": "_safeFalse()", "0x3133f2a7": "outstandingBalance()", "0x773c84ee": "exec(address,bytes,uint256,uint256)", "0x9070b18d": "_getAllRevisionBlockNumbers(bytes32)", "0x476e04c7": "NewMessage(string)", "0x3b91ceef": "setMax(uint256,uint256)", "0xe6cb9013": "safeAdd(uint256,uint256)", "0xc2038560": "setOutcome(bytes,bytes)", "0xbed34bba": "compareStrings(string,string)", "0xba8661a2": "TimestampScheduler(address)", "0x4c4aea87": "getReleaseData(bytes32)", "0x1177892f": "getBalanceByAdress(address)", "0x126a710e": "dnsrr(bytes32)", "0x60913244": "botOnSale(uint256,uint256)", "0x5731f357": "oraclize_query(uint256,string,string,string)", "0x3e58c58c": "send(address)", "0x2187a833": "setGreenToken()", "0x5f09952e": "voteAllowTransactions(bool)", "0xf2b26d8f": "nextEtherForSale()", "0x1f201e39": "etherandomExecWithGasLimit(bytes32,bytes32,uint256,uint256)", "0x43d726d6": "close()", "0x6cdf4c90": "ownerSetMinBet(uint256)", "0xe6e8c692": "computeResponseFirstHalf(uint256,uint16)", "0xd1d80fdf": "setAddr(address)", "0x6da1833c": "getInstitutionByName(string)", "0x7682e6ff": "getTrustSetting(address)", "0x8f6f988c": "setUltimateOutcome(bytes)", "0xe299beb3": "SimpleIndex()", "0xa2bb5d48": "get_username(address)", "0x780900dc": "create(uint256)", "0x78710d37": "seven()", "0x2b20e397": "registrar()", "0x4094ef5e": "addDataRequest(string)", "0xc630f92b": "canEnterPool()", "0xdd114c22": "publish(address,uint256,address,uint256)", "0x57006864": "checkBetParity(uint8)", "0x45788ce2": "prev(address)", "0xee8ff562": "setMaxProfit()", "0xdc63a62c": "getFileListHead()", "0x9447fd0a": "until()", "0xb303dcbd": "Owned()", "0x0ecaea73": "create(address,uint256)", "0x20339891": "addGridMember(address)", "0xc8c01a55": "request(address,uint256)", "0x76f10ad0": "getSnapshot(uint256)", "0xc06c4474": "get_burned(bytes32)", "0x67cb61b6": "getChoice()", "0xca708230": "funnel()", "0x08b7fa31": "PriceFeed()", "0xac6bc853": "startSpin()", "0xf4bbfd6a": "scheduleCall(bytes,bytes)", "0xab73e316": "next(address)", "0xba0179b5": "confirm(uint256)", "0x1e62be25": "Bytes32Passer()", "0xb950556a": "setThingValid(bytes32[],bool)", "0xb61c0503": "fireEventLog1()", "0x79a85e6c": "getProductInfo(uint256)", "0x959ac484": "push(uint256)", "0x78e80b39": "UserGetPrize()", "0xff74927b": "strConcat(string,string)", "0xd207e757": "ownerSetOraclizeSafeGas(uint32)", "0x5819dde2": "getNumbersFromBytes(bytes3)", "0xf34ed4e6": "RanDAOPlus(address)", "0x3943807b": "insert(bytes,bytes,int256)", "0x38cc4831": "getAddress()", "0x12a7b914": "getBool()", "0xa4fd6f56": "isEnded()", "0x6c86888b": "testTrade(address,uint256,address,uint256,uint256,uint256,address,uint8,bytes32,bytes32,uint256,address)", "0x0bebd0f9": "addAddressToGeneration(address,uint256)", "0x003074ff": "getFrontend()", "0x2776a859": "computeResponseSecondHalf(uint16)", "0xadfe6b80": "InvestAdd()", "0x6981b5f4": "getLength(string)", "0x4a994eef": "setDelegate(address,bool)", "0xbac1e9f6": "getChannelSize(address,uint256)", "0xbdfdb519": "accept(string,uint256,uint16)", "0x68f5aa0f": "setShareholderDB(address)", "0xafb95eed": "logApproval(address,address,bytes32)", "0xae152cf4": "oraclize_query(string,string,uint256)", "0xe3b26a8c": "SocialNetwork()", "0x54204ad4": "triple()", "0xbc8f3bcb": "ZeroDollarHomePage()", "0xf0350c04": "transfer_ownership(address)", "0xfde9ba41": "transfer(bytes,address,uint256)", "0xfa2acd87": "G(uint64[16],uint256,uint256,uint256,uint256,uint64,uint64)", "0x3a76abff": "_eraseNode(uint256,bytes32[],bytes32)", "0x0acf473b": "AdminCloseContract()", "0x299e7318": "resolveVoting()", "0xb414d4b6": "frozenAccount(address)", "0x8d375da2": "testMakeItFail()", "0x6e8dad74": "retrieveAccountBalance(bytes,bytes)", "0xd3aa831f": "testOwnedTryAuth()", "0x13220305": "doTransferOther(address,address,address,uint256)", "0xb0c7f709": "kingAutomaticCollectFee()", "0x2dabbeed": "reclaim(uint256)", "0x5c19a95c": "delegate(address)", "0xbc2a4dd6": "doBalanceOf(address)", "0x8e7ea5b2": "getWinner()", "0xdb37e42f": "multisetProofType(uint256[],address[])", "0xa6b197aa": "Order(address,uint256)", "0x8cf4dbfb": "collectBalance()", "0xd0821b0e": "bet(uint8)", "0xa02b161e": "unregister(uint256)", "0x09dd0e81": "getBlockchainHead()", "0x8a0807b7": "indexOf(string,string)", "0xe3914699": "dEthereumlotteryNetWinners(address)", "0x44d75fa9": "updateMinorTree(bytes32)", "0x3c21db0a": "theGames(uint256)", "0xf0e959f9": "TokenSales(address)", "0x696bda86": "submitProposal(uint256,bytes)", "0x3b343a13": "getNodeAddress(bytes)", "0x2812f8b8": "FutureCall(address,uint256,uint16,address,bytes4,bytes,uint256,uint256,uint256)", "0xbf32bf97": "FailGuyTax()", "0x89ced196": "setNotUpdatable(bytes32)", "0xb94e962a": "allocateTickets(uint256)", "0x7a479160": "getRequestArgs(uint256)", "0x5a825cbb": "getPayment(uint256,uint256)", "0x4ca8b0d0": "registerExistingThrone(bytes,address,uint256,uint256)", "0x82afd23b": "isActive(uint256)", "0x6ebf10fe": "storeHeader(bytes,address)", "0x1437f9a3": "Set_your_game_number(uint16)", "0xd98d011d": "getCandidateKey(bytes,bytes,bytes,bytes)", "0x8b676ae8": "scheduleCall(address,bytes4,uint256,uint256,uint8,uint256,uint256)", "0x90fd53ec": "farmTile(uint8,uint8,int8)", "0x46e44f63": "getCheckRecordTS(bytes)", "0x1de38038": "makercoin(uint256)", "0xc038a38e": "totals()", "0xfa80918b": "computeNodeId(bytes,bytes)", "0xc76a4bfb": "relayReceiveApproval(address,address,uint256,bytes)", "0x2406cedb": "setPackageOwner(bytes32,address)", "0xb7297cf3": "gameSettings()", "0xe94acf0e": "TinyRouter(address)", "0x4a2b0c38": "DividendProfit()", "0x0e3f732a": "TheGame()", "0xd62457f6": "callValue()", "0x4961b40c": "getReleaseValidator()", "0x540cafe0": "storeHeaderWithFee(bytes,int256,address)", "0x7ff729fc": "fillUpProject(uint256,uint256)", "0x253459e3": "feesSeperateFromBalanceApproximately()", "0x930a80b4": "testAuthorizedSetPackage()", "0xb3cb8885": "nextUnderdogPayout()", "0x62c7855b": "getConfigBytes(bytes32)", "0x4f28af6a": "handleBet(uint256)", "0x103f9251": "transferFrom(address,address)", "0x9b19251a": "whitelist(address)", "0x9928811b": "testBroken()", "0xb33a8a11": "setTokenReference(address)", "0x27f06fff": "requestFillUp(uint256)", "0x2f570a23": "test(bytes)", "0x96ef7aa0": "cash_transfered(string)", "0x3983d5c4": "calcBaseFee(uint256)", "0xec0f1025": "testBitsOrSuccess()", "0xd35f4a99": "mint(int256,address,uint256)", "0x09dfdc71": "currentPyramidBalanceApproximately()", "0xac4e73f9": "proposeReverse(string,address)", "0xac4bd53a": "currentLeader()", "0x5a2ee019": "m()", "0xeba36dbd": "setAddr(uint256,address)", "0x0358d965": "addPayout(uint256)", "0xd7206124": "setInvestorLock(bool)", "0xe916d0f0": "doBalance(address)", "0x67c2a360": "authorizeUser(address)", "0x828d671c": "dyn_sig()", "0xaf6fe8e2": "testGetToken()", "0x283a4576": "Tomeka()", "0x8ac0ca36": "buyViaJohan()", "0xcc872b66": "issue(uint256)", "0xd826f88f": "reset()", "0x2aa3177a": "self_store()", "0x53b7b2e9": "cEthereumlotteryNet(bytes)", "0xce88b145": "getAccount(uint256)", "0x1fa03a2b": "isApprovedFor(address,address)", "0xe42d5be0": "getPaymentOf(address)", "0xb722a9ef": "getPreviousShareholder(address)", "0xfadf87b1": "testGetBitsSuccess()", "0xd26c8a8a": "coinBalance()", "0x30ccebb5": "getStatus(address)", "0x47799da8": "last()", "0x4a5db3b5": "authorizeAddress(address)", "0x22686250": "index(int256,uint256)", "0x07ef99a0": "demintTokens(int256,address,uint8)", "0xea2d4cf8": "__DeployerFunctions(address,address,uint256)", "0x092a2e37": "multiAccessAddOwnerD(address,address)", "0x671fa0a0": "Inscription(string)", "0xa10edc55": "GeneralPurposeProfitSplitter()", "0xd9c67404": "getMerkleRoot(bytes)", "0xdc419fd8": "cancelOrder(bool,uint256)", "0xc9734ebd": "WatchLastPayout()", "0xc7d6faf1": "easyPropose(address,uint256)", "0xfe63300a": "registerExternalBill(uint256,address,address,uint256,uint256,uint256)", "0xd3437fe0": "assertFact(uint256,bytes)", "0x5fb3e119": "Auction()", "0x665beae7": "ExecutableBase(bytes)", "0xc8bb73ef": "testGetBitsFailIndexOOB()", "0xc1d4f708": "getMwLength()", "0x22b0f6ee": "getStatusOfPayout(uint256)", "0x21520c5f": "calculatePayout(uint8,bool,uint256)", "0x66e98c31": "createCoin(string,uint256,uint256,string,string,address)", "0x7b352962": "isFinished()", "0x48d9614d": "GetFee()", "0xfe0d94c1": "execute(uint256)", "0xe4547f98": "documentExists(bytes)", "0x4e10c3ee": "transferWithoutReward(address,uint256)", "0x58ae8bcf": "voteInMasterKey(address)", "0x1dcb304b": "fipsGenerate()", "0xb595181f": "ShapeshiftBot()", "0xe02426c1": "getSignatureHash(bytes4,uint256)", "0x67546967": "EthBtcEscrow()", "0x85b31d7b": "myInfo()", "0xaa677354": "register(address,address)", "0x1d2e2cc4": "ENS()", "0x1097e579": "Enter()", "0x13a396d8": "getRequiredDeposit(bytes)", "0x6df3edef": "getSavedBytes()", "0x09b30ed5": "afterExecute(address)", "0x718e6302": "play(string)", "0x8e46fbb2": "testBitsXorFailIndexOOB()", "0x0d87a7c0": "WLBDrawsDB()", "0xbbe42771": "closeDeed(uint256)", "0xedfbf7b6": "setVotingDeadline(uint256)", "0x299e6b07": "Wallet(address)", "0x5cbc85d0": "returnBounty(uint256)", "0xe5225381": "collect()", "0x94f60a63": "getKudosLeft(address)", "0xd6960697": "confirmPurchase()", "0x4a1f0bf6": "inheritToNextGeneration(address)", "0x244ded7a": "ChangeOwnership(address)", "0x39b35753": "authCancel(address)", "0x75cb2672": "configure(address)", "0x938ae4cc": "testThrowDisownNotTransferable()", "0x04fc11d5": "getActual()", "0xacab021c": "getTOS(address)", "0x812cddf2": "getSavedString()", "0x8ae475a9": "notorize(string)", "0xb1d05422": "SendEmail(string,string)", "0x0fffbb54": "changeRankingSize(uint256)", "0xb6ed9f15": "PFOffer(address,address,bytes,uint256,uint256,uint128)", "0xda333ca6": "payOut(uint256)", "0x652f1f16": "addSignature(string)", "0x983b2d56": "addMinter(address)", "0x5e1936d4": "testThrowSetNotTransferableNotOwner()", "0x4ac1ad78": "getWeekday(uint256)", "0x6ba0b4f2": "isKnownSelector(bytes4)", "0x7c4c27c8": "isThisPuritanicalVersion()", "0x7ae2b5c7": "min(uint256,uint256)", "0x63bd1d4a": "payout()", "0x3fd94686": "changeEligibleDonkeys(uint256)", "0x7fe0518a": "asyncSend(address,uint256)", "0x5a8dd79f": "getDesignatedCaller(address,uint256)", "0x2635f4de": "registerLibrary(bytes,address)", "0x1335ff36": "createEventAndMarketMaker(uint256,uint256,uint8,uint32,address,uint256,uint8,uint16,uint256)", "0x181be00d": "getValue(uint8)", "0x9c1500f0": "registerMany(address,uint256,int256,uint256,bytes,address,bytes)", "0x16f9ce49": "_slotCommitNew(address)", "0x8ca4eef6": "getBuild(bytes32)", "0x8ee21b8e": "get_default_keys()", "0xa66f7ad6": "signRelease(uint256)", "0x414053be": "best_adjustment_for(bool,uint128)", "0x83a51ad0": "oraclize_setConfig(bytes32)", "0x262c0b72": "getPayoutFreezePeriod()", "0x499af77c": "current_spin_number()", "0x4209fff1": "isUser(address)", "0x6e1b6bcc": "checkMyBet(address)", "0xb46300ec": "send()", "0x6b1cb549": "orderMatch(uint256,uint256,uint256,int256,uint256,uint256,address,uint8,bytes32,bytes32,int256)", "0x58d9fa04": "addUser(uint256,address)", "0x24c93343": "error(string)", "0xa95d017d": "getRevisionBlockNumber(bytes32,uint256)", "0x46af23f5": "InstantLottery(address,address,bool,address)", "0x29ef56b1": "getAskOrderBookStats()", "0xe97db66e": "setJackpot()", "0x4b59e880": "puzzle(address,bytes32,bytes32)", "0xc7f86c37": "withdrawFundsRP()", "0x57d4021b": "nextPayoutWhenPyramidBalanceTotalsApproximately()", "0xf1c760ae": "fixBalanceInternal(address)", "0x0908178f": "NoFeePonzi()", "0x22ebb3ac": "DieselPricePeg()", "0xb39a64cd": "getNumCalled()", "0x1c02708d": "killContract()", "0x65228934": "setOperationsCallGas(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)", "0x3397ca17": "numBalanceRecords(address)", "0xe436bdf3": "Draws(uint256)", "0xaf55bba0": "removeRegistryFromTagsIndex(address)", "0x11103599": "Token_Offer(address,address,uint16)", "0xb3a2a6c0": "setOfficialWebsite(string)", "0xb06eb03f": "DSEasyMultisig(uint256,uint256,uint256)", "0x775c300c": "deploy()", "0xb1c6517a": "LookAtNumberOfPlayers()", "0xc19d93fb": "state()", "0xac9873c7": "CanaryV7()", "0x750cae6a": "enableBetting_only_Dev()", "0xdf3c8620": "num_challenges()", "0xbb00fc55": "bookEarnings()", "0x0bd2ae1c": "ERW()", "0x580bdf3c": "disableBetting_only_Dev()", "0x5c3f9765": "endDateClose()", "0xc4128b6d": "upgradeCount()", "0x140b4465": "executeSpendingRequests()", "0xfaa1a8ff": "getOwnedBot(address,uint256)", "0x40e58ee5": "cancel(uint256)", "0xf4ea95b9": "validateReleaseVersion(uint32[3])", "0x2ba0b09f": "AddNewCategory(bytes4,uint8,uint8,address)", "0x55db4092": "setTOS(address,bool)", "0xf9cc0605": "getAvailable()", "0x3f2f46b4": "revealRock(string)", "0x2af7ceff": "testPrice(uint256)", "0xcaaf2dd7": "getInitialAnswerResult(uint256)", "0x6f36ce79": "insert_deal(address,address,uint64,uint128,uint32)", "0xa18c751e": "set(bytes,bytes)", "0x4d536fe3": "doit()", "0x3197cbb6": "endTime()", "0xb83069c5": "getStemPrice()", "0x3f15457f": "ens()", "0x81ebdeea": "testThrowCreateWithNonceRetracted()", "0x249b4d0b": "removeTrustedIssuer(address,bytes)", "0xe7b48f74": "get(int256,address)", "0x089d5c4a": "repr()", "0x3a9e7433": "scheduleCall(bytes4,uint256,uint256,uint8)", "0x504f1671": "getSize(address)", "0xf3a44fe1": "withdrawForWorkshop()", "0x7a837213": "setAllowedAccount(address)", "0x4551b1d7": "ProxyPayment(address,address)", "0x5dac1601": "SimpleStablecoin()", "0x87914c6f": "prolongateContract()", "0xe3ceb06d": "YesNo(bytes32,address,string,address,uint256)", "0x42a745cb": "testBitEqualSuccess()", "0xaa8dea8c": "fipsAddToLedger(bytes20,address,bytes)", "0x986dcd4d": "setCycleLimit(uint256)", "0x1e44c112": "find_strike(uint64,uint32,uint32)", "0x1ecfe64d": "_jSub(uint256,uint256,uint256,uint256)", "0x4ae85627": "grindUnicorns(uint256)", "0x200ebe34": "addTokensToGive(address)", "0x7b632c41": "TimestampScheduler(address,address)", "0x979b6f6f": "RoundInfo()", "0xb3ea3924": "PointlessCoin(int256,uint256,string,uint8,string,address)", "0x1d834a1b": "insert(uint256,uint256)", "0x931df75f": "validateProposedThroneName(bytes)", "0x6189be15": "columnround(uint256,uint256)", "0xdf5dd1a5": "addOracle(address)", "0x22d8cf5b": "CheckUserVote(uint8,uint8)", "0xdd62ed3e": "allowance(address,address)", "0xc0eb2325": "scheduleTransaction(address,bytes,uint256)", "0x038461ea": "getCertifiedStudentsCount()", "0x9ee035c9": "lookupCanonicalFormat(bytes)", "0x0ab58ead": "SingularDTVFund()", "0x550ed1f0": "getMaxBetAmount()", "0x6e63015c": "getCertifiersCount()", "0xbbd4f854": "buyShares(bytes32,uint8,uint256,uint256)", "0x306b031d": "getGenerationEndAt(uint256)", "0x1bd9c46e": "setImporter()", "0xc80c28a2": "getNumberOfParticipants()", "0xcb553ac9": "sweepWizardCommission(uint256)", "0x6389654e": "changeDailyWithdrawalLimit(uint256)", "0xc0a963c9": "notifyWinner(address,uint256)", "0x3c335b0e": "getRetractable(bytes20)", "0x017972af": "getNumbersFromHash(bytes32)", "0x07da68f5": "stop()", "0x1e8c72b4": "incrUserAvailBal(address,uint256,bool)", "0x0df71602": "setWinner(uint256)", "0x85e5bb3a": "Security_AddPasswordSha3HashToBankAccount(bytes32)", "0x2ade6c36": "getNodeAddress(bytes32)", "0xb33926cb": "owner_withdraw(uint256)", "0x57764094": "getRate(uint256)", "0x13827950": "getShareholderDB()", "0x26826bf8": "setImage(bytes)", "0x76d438b0": "sendReward(uint256,uint256)", "0x51d6e547": "getNonce(bytes)", "0x4f24186a": "newProposal(string)", "0x1a695230": "transfer(address)", "0xe820a32f": "vetoPayout(uint256,uint256)", "0xfb3a1fb2": "getReleaseDb()", "0x6bae05cf": "preRegister(address)", "0xae2df7b3": "setImporterBank()", "0x56d73ad1": "getCertifierDb()", "0x8396392d": "add(string,string,string,address)", "0xeb08b304": "changeMeatProvider(address)", "0xb60e72cc": "log(string,uint256)", "0x76999896": "KingOfTheEtherThrone()", "0xea851885": "buyStake(bool)", "0xa5982885": "assertFalse(bool)", "0xfbf1f78a": "unapprove(address)", "0xc2ba5b40": "getPackageData(string)", "0x83d51a38": "concatString(string)", "0x68af4971": "registerListening()", "0x14ba5c09": "getDay()", "0xe4849b32": "sell(uint256)", "0x44dd4b3b": "lookupGeneration(uint256)", "0x3fda1281": "get_keys()", "0x09241200": "wasSuccessful()", "0xbc9147a4": "Foundation()", "0xe33734fd": "changeProposalDeposit(uint256)", "0x2cce4abe": "_finishNoCallback()", "0x45e965cd": "strConcat(string,string,string,string)", "0xd4245e5b": "transferringETH(address)", "0x10082bff": "getActorBillXdetail(address,uint256,bool)", "0xc831391d": "getPoolOverlapSize()", "0x15e33901": "digest(bytes,uint256)", "0x5f0edfb8": "create(bytes,bytes32,bytes1)", "0xadd4c784": "getResult(bytes32)", "0x3233c686": "claimerDeposit()", "0x187a62d5": "voteEmergencyWithdrawal(bool)", "0x5404bbf7": "getEntropy()", "0x3f5b7675": "periodTwo()", "0x7ec0f30d": "ack(string)", "0xd30a512e": "betOnColumnOrDozen(bool,bool,bool)", "0x9fcbc738": "setIntermediate(address)", "0x539e2bfb": "secondChainedCallback(uint256)", "0xe724529c": "freezeAccount(address,bool)", "0x5aa97eeb": "getMarkets(bytes32[],address)", "0xc51be90f": "query_withGasLimit(uint256,string,string,uint256)", "0x531b97d7": "oneCentOfWei()", "0xeace4827": "player_make_bet(uint8)", "0x9c851ebc": "new_entry()", "0x98c9cdf4": "getMinimumCallGas()", "0xd8f012c6": "StatelessFactory(string,string,string)", "0xceeafd9d": "withdrawFundsAdvancedRP(address,uint256,uint256)", "0xd6d7d525": "get(bytes)", "0xbf55486b": "Tanya()", "0xd35ada32": "addParticipant(address,address)", "0xa8239d0b": "getPrice(string,address)", "0x12514bba": "transfer(uint256)", "0xb00140aa": "getHash(bytes)", "0x36f66528": "EtherDelta(address,uint256,uint256)", "0x279e0912": "getDownloadPrice()", "0x8173b813": "setNumCities(uint256,uint256)", "0xb98fdc36": "IconomiToken(uint256,string,uint8,string,uint256)", "0x3c7a3aff": "commit()", "0xcac77df7": "__transferFromToICAPWithReference(address,bytes32,uint256,string)", "0xfbeaebc6": "murder()", "0x2fa7cbfb": "getExecCost(uint256)", "0xe44d3084": "testFailure()", "0xede8acdb": "startAuction(bytes32)", "0xd5544f94": "getFundsAndAvailable(address)", "0x3824d8ee": "buy100DaoFor1Eth()", "0xbe3945e4": "getFee(address,address,uint256)", "0x1917ab5c": "activate(string)", "0x23509e69": "donkeysEligibleForFees()", "0xf460590b": "updateSigner(address,bool)", "0xc55c1cb6": "queryN_withGasLimit(uint256,string,bytes,uint256)", "0xd96d7ea2": "PRE_EXECUTION_GAS()", "0x1f7b6d32": "length()", "0xacf8bf2a": "channelCount()", "0x18968a03": "finalize(uint256,address,address)", "0x5674a3ed": "runLottery()", "0xe3280126": "addOrder(string,bool)", "0x32afa2f9": "claimEtherOwner(uint256)", "0x355474d2": "commitReading(address)", "0x00601d6c": "board(uint256,uint8,uint8)", "0x2667f407": "__proxy(address,bytes)", "0x79716e43": "confirmTransaction(bytes32)", "0x272cda88": "EternalDB()", "0x76ca0c77": "scheduleCall(address,bytes,uint256,bytes,uint256)", "0x338b5dea": "depositToken(address,uint256)", "0xfa93f883": "getMinute(uint256)", "0xafc4a982": "PathCost(uint16,uint32)", "0xf7d97577": "setPrice(uint256,uint256)", "0x842bc37b": "GetSmallCotractIndex(address)", "0x75f40f40": "underdogPayoutFund()", "0x23a1c271": "setPongval(int8)", "0x02571be3": "owner(bytes32)", "0xf79b22e0": "betOnATeam(uint256)", "0x10cf5d47": "awaitingPayout()", "0x1b00fe51": "testHypothesis()", "0xd449ce7c": "Administered()", "0x455259cb": "getGasPrice()", "0x975057e7": "store()", "0xdfdb5f17": "doBurn(address,uint256)", "0xaa497b9d": "scheduleCall(address,uint256,bytes,uint256,uint256,uint8)", "0xcabfb934": "replace(address)", "0x1f1f5e76": "addValueToContribution(uint256)", "0xa0eda9f2": "_transferFee(address,uint256,string)", "0xa8d95fb2": "claim(address,string)", "0x03ee8f08": "getCoeff(uint16)", "0x9872a20a": "registerUInt(address,uint256)", "0xb20d30a9": "setDailyLimit(uint256)", "0xe116b17e": "getKudosLeftForProject(address,address)", "0xf7c9f74a": "insert_contribution(address,uint256)", "0xc7a1865b": "play(bytes32)", "0x356594ab": "EtherTransfer()", "0xe22b0c46": "verify(uint256,uint256,uint8,bytes,bytes)", "0x2fea7b81": "getIdentity(address)", "0x6fa8de90": "changeMeatParameters(uint256,uint256)", "0x37c390e3": "allow_move(uint16)", "0xd22c391a": "validateProposedThroneRules(uint256,uint256,uint256,uint256,uint256)", "0xf6d339e4": "setAddress(bytes32,string,address)", "0xd7cc8362": "isLatestMajorTree(bytes32,bytes32)", "0xdd012a15": "setIt(uint256)", "0x254c91b3": "testBitNotSetSuccess()", "0x47e40553": "nextRound()", "0xa6b206bf": "doSomething(uint256)", "0xac996e7e": "resolvePledging()", "0x71e2d919": "lol()", "0x07d5b826": "buyAllOutcomes(bytes32,uint256)", "0x5f68804e": "SimpleLotto()", "0xa510f776": "setCompany()", "0x0d48e8d0": "doBalance()", "0xd21d7950": "changeGasLimitOfSafeSend(uint256)", "0x3358d2d3": "buildDSTokenFrontend()", "0xec93cfae": "FountainOfWealth()", "0x65b1fdf4": "scheduleIssuePOIs()", "0xdc3080f2": "spentAllowance(address,address)", "0xd1f0bb2d": "populateAllowedFreeExchanges()", "0xd591221f": "testTransfer()", "0xf24b5779": "removeTrustedIssuer(address,string)", "0xed4b1d0d": "scheduleTransaction(uint256)", "0xa83627de": "updatePeriod()", "0xf597a499": "UserDatabase(uint256)", "0x21f8a721": "getAddress(bytes32)", "0x5548c837": "Deposit(address,address,uint256)", "0x55b775ea": "setFeed(address)", "0x01b869f1": "release(uint32,uint32,uint32,bytes)", "0x609ff1bd": "winningProposal()", "0xdf98ef33": "getResource(bytes,uint256,bytes)", "0xd39eb301": "getStatus(uint8,uint8)", "0x2cc0b254": "init(address,bytes32)", "0x4228974c": "Videos()", "0xc431f885": "addToContribution()", "0x00e43ee9": "setMigrationStatus(uint256,address)", "0xd9f8a4e2": "calcCurrentTokenPrice()", "0xb9e6f1d9": "get_amount()", "0x6b3a87d2": "WatchWinningPot()", "0xef4592fb": "getResult(bytes)", "0x41b9dc2b": "has(bytes32,bytes32)", "0x3b9901cc": "getChannelsByRanks(address,uint256,uint256)", "0x83876bc9": "newProposalInWei(address,uint256,string,bytes)", "0x50a3bd39": "enterPool()", "0xc976bbbb": "_compare(int256,bytes2,int256)", "0x0e47c76f": "rotate(uint64,uint256)", "0x6f85c7e4": "WAITING_PERIOD()", "0x7075b1d8": "latestMonarchInternal()", "0x9209b3c0": "getCrtDetails(bytes)", "0x305075db": "NormalizeRanks()", "0xb6b55f25": "deposit(uint256)", "0xf09ea2a6": "offer(uint256,address,uint256,address)", "0xf1320af2": "exempt(address)", "0xc813c30e": "testThrowSomething()", "0x4faa2d54": "getTimeElapsed()", "0x22017c5f": "DSTokenBase(uint256)", "0x6637b882": "setDao(address)", "0xd0b52156": "getIpfsHash(address,address)", "0x13bd4e2c": "_prepareAndSendReward()", "0xdb7ca38a": "XaurmProxyContract()", "0x1bad1d2e": "monitorWallet(address)", "0x691f3431": "name(bytes32)", "0x3169ff3e": "LooneyLottery()", "0x446a7974": "Fokitol()", "0xdc3ab866": "checkEarnings(address)", "0xfad9bf9e": "storeBlockWithFeeAndRecipient(bytes,int256,int256,bytes,int256,int256)", "0xaf408d89": "setStatus(bytes)", "0xd02bf162": "spinTheWheel()", "0x9a36f932": "feeDivisor()", "0xca77ab8a": "getNextFile(bytes)", "0xc8e49707": "activateExportFee(address)", "0x502414e4": "marketMaker(string)", "0x78ec81a0": "sendEarnings(address)", "0x14167bf0": "oraclize_query(string,string[])", "0xba13a572": "lottery()", "0x299ed37a": "emergencyCall()", "0x6ec3af26": "addTrustedIssuer(address,bytes)", "0x3d69b403": "isOutcomeSet(bytes)", "0x9a19a953": "setInt8(int8)", "0x7817a60f": "acceptMember(address,string)", "0x1e223143": "getFirst()", "0x5437b39b": "hasUnprocessedDividends(address)", "0x8f0c724c": "setOperationsCallGas(uint256)", "0x3c925f16": "getAccountHolder()", "0x477801b1": "getLastRoundResults_by_index(uint256)", "0x3d21aa42": "sendApproval(address,uint256,address)", "0xd2b8035a": "draw(uint256,uint256)", "0x19c32e0b": "hmacsha256(bytes,bytes)", "0x28f90e4b": "Etheramid2()", "0xe87df70e": "fivetimes()", "0xc028df06": "offer()", "0xacb6c69b": "setTrustedClient(address)", "0xac7ffae3": "updt(uint256,string,uint256,uint256,string,string,address)", "0x0e5ffb3c": "hashVersion(uint32,uint32,uint32,string,string)", "0x4ac6b2be": "getCheckRecordCreator(bytes)", "0x93dafba2": "getSubpot(uint256)", "0x592685d5": "getWindowStart(address,address)", "0xc24a0f8b": "endDate()", "0x3f9b250a": "getDocument(uint256)", "0xd845a4b3": "request(uint256)", "0xa5bfa9a9": "claimToken(bytes32)", "0x8e3957d9": "RandomNumber()", "0x62fb09b2": "getRefDescr(uint256)", "0x9f35d3b2": "start(string,string,uint256,uint256,uint256,uint256)", "0xea1bf386": "getNextSellerBOTdata(uint256)", "0xe27fe50f": "startAuctions(bytes32[])", "0x031d973e": "closeMarket(bytes32)", "0x754dea40": "setBackendOwner(address)", "0xd83a8d11": "testProposing()", "0xf24a534e": "Oracle()", "0xd08275f1": "WolframAlpha()", "0x932db761": "profitsFromBitnationDebitCard()", "0xd96aee49": "MultipleConstructorTest()", "0x4594d06a": "delMinter(int256,address)", "0xb9f256cd": "newProposalInEther(address,uint256,string,bytes)", "0x1d2bca17": "MyToken(uint256,string,uint8,string)", "0x6edbd134": "hasHash()", "0x847f8a10": "Refund(uint32)", "0xb0604a26": "schedule()", "0x6676871d": "reserved_funds()", "0x05888fcd": "tradeBalances(address,uint256,address,uint256,address,uint256)", "0xe771066f": "marriageProof(bytes)", "0x7e32a592": "repairTheCastle()", "0xbab2f552": "currentCycle()", "0x45a3b0bf": "resolveFailPledge()", "0x070a888f": "updateRewardDuration(uint256)", "0x20d9822e": "setAnyoneCanCall(address,string,bool)", "0x224993c2": "setTimeBlock(uint256)", "0x01bd4051": "disown(string)", "0x4bc2a657": "setVoter(address)", "0x522103fa": "changeUnicorn(uint256,address)", "0xc988d70f": "getDailyWithdrawLimit()", "0x2f7f3ecf": "findNextHour(uint256,bytes)", "0x1b769e74": "testThrowsRestartNotUpdatable()", "0x4d561721": "etherandomSetNetwork()", "0x92a781d8": "changeBaseValue(uint256)", "0xf0a78538": "scheduleTransaction(uint256,bytes)", "0x64ef212e": "proxyTransferWithReference(address,uint256,bytes32,string)", "0x97c3ccd8": "ban(address)", "0xdeb6930c": "PriceTicker()", "0x6cc5fdaa": "setBytes32(bytes,bytes)", "0x92c8eb96": "DSFalseFallbackTest()", "0x6534b4e2": "IsPayoutReady__InfoFunction(bytes32)", "0x15e812ad": "getBaseFee()", "0xf5b53e17": "getInt256()", "0x081bf263": "isOOB(uint8,uint8)", "0xd2fb8787": "recordExists(bytes)", "0x86c57fcc": "b32ToBytes(bytes)", "0xe3a199d6": "testThrowCreateNewRevisionNotUpdatable()", "0x57cfeeee": "transfer(address,uint256,bytes32)", "0xb72e717d": "fromAddress(address)", "0x61b20d8c": "retrieveFunds()", "0xf4b103d4": "SimpleStorage(uint256)", "0xe2056c46": "ExtraBalToken()", "0xc3ee6311": "lockAndCall(string)", "0x136af582": "next(bytes,bytes,bytes,bytes,bytes,bytes,bytes)", "0xc5d5997c": "changeSubUser(address,address)", "0xf8018a79": "prepend(address,address)", "0xf6b4dfb4": "contractAddress()", "0x9450b1c8": "addCharityFundation(string,string,string)", "0xd1a8d447": "get_all_bet_values()", "0x66d8c463": "reveal(bytes32,string)", "0xbb963c8a": "transferLibOwnership(bytes,address)", "0xd56b2889": "finish()", "0x9fb25d9e": "LeaderMessage()", "0x6ad50ed4": "investmentEntryInfos()", "0xa4d575ce": "_forward(address,bytes)", "0xa3ec5616": "next(bytes,bytes,bytes,bytes,bytes,bytes,bytes,uint256)", "0x0db73c72": "noevent()", "0xf449619e": "collectPrize(uint256)", "0x1afccfa5": "Proposal(address,address,address,bytes,bool)", "0x4788cabf": "getContractId()", "0x4112987c": "strConcat(string,string,string)", "0x928a00d2": "deleteCoin(uint256)", "0xd9ec0508": "testThrowTransferNotEnabled()", "0x9aaf442c": "applyCensorship(uint256)", "0x49407a44": "claimEther(uint256)", "0x4fcf8210": "eraseRecord(bytes32)", "0x5ed84aa6": "getNymCenterAPIURL()", "0xa3c2c462": "totalReceived()", "0x5938748e": "changeVotingRules(address,address,uint256,uint256,uint256)", "0x6fc9d5e4": "changeCompareTo(uint256)", "0xe2ee9941": "tap(bytes20)", "0x0ff4f160": "oraclize_query(uint256,string,string[1])", "0x43ec3f38": "toSliceB32(bytes32)", "0x3288eb0b": "ChineseCookies()", "0x373c98a2": "authCall(address,bytes32)", "0x6ea056a9": "sweep(address,uint256)", "0x8dc45377": "getDuel1(uint256)", "0xd4625a3a": "equals()", "0x616fca9b": "adopt(address)", "0x32cea83e": "birth(bytes)", "0x002a5cc9": "getTicketHolders(uint256)", "0x31119b4d": "changeDeveloper(address)", "0x69569a51": "setFrontend(address)", "0xb7e24979": "addThing(bytes)", "0x164e68de": "withdrawFees(address)", "0x42bf4431": "orderMatchTest(uint256,uint256,uint256,int256,uint256,uint256,address,address,int256)", "0x5dd672ec": "latestBid()", "0x45fe6e2a": "Scheduler()", "0x55cc4e57": "setIssuer(address)", "0x1df5e755": "Etherandom()", "0x8f70009d": "id_for_address(address,address)", "0x39b50688": "cancelSellOrder()", "0x40953102": "scheduleCall(address,uint256,bytes,uint256,uint256,uint8,uint256)", "0x677913e9": "setAmount(int32)", "0x66671c71": "BaseScheduler(address,address)", "0xfe71aec5": "LittleCactus()", "0x879d46fd": "DAOTrust(address,address,bytes,uint256,uint256,uint128)", "0x3b143184": "Congress(uint256,uint256,int256,address)", "0x7370a38d": "getNumPackages()", "0xfee35ff8": "newInvest(uint256,address,uint256)", "0x57dc9760": "DaoChallenge()", "0x8ac78c80": "Docsign()", "0x76d66f5d": "_Transfer(address,address,bytes32)", "0x9dc2c8f5": "fireEventLog4Anonym()", "0x43114842": "acceptChallenge(uint256,uint256,uint256)", "0x2043285d": "getMarketMakers()", "0xfb32f4f5": "ARK_FLAGGER_1_00()", "0x69431ab6": "TokenCreation(uint256,uint256,address,string,string,uint8)", "0x045c6ce0": "voteForProposal(uint256)", "0xa140e79c": "setMinimumDebatePeriod(uint256)", "0x331a72d1": "getRetractable(bytes32)", "0x87cc1e1c": "setExporterBank()", "0x7183616c": "notarize(string)", "0xce79add1": "givableBalanceOf(address)", "0x6f0cfab6": "DNSResolver()", "0x1c2f38ff": "paid(uint64)", "0xb7de47d3": "getIndex(uint256,uint256)", "0x04a2b2c2": "testOwnerCanBreach()", "0x65a4dfb3": "oraclize_query(uint256,string,string,string,uint256)", "0xf2ddc772": "confirm(bytes)", "0xa00ce377": "getIsContractValid()", "0xfe9fbb80": "isAuthorized(address)", "0xbbd39ac0": "coinBalanceOf(address)", "0xa4fde8bc": "player_declare_taking_too_long()", "0xd052fbf6": "getHistory(string,uint256)", "0xd205ad7d": "proposeDissolve(bytes)", "0x5a3b7e42": "standard()", "0xf27197ab": "getIsAvailable()", "0x00a94b6e": "oraclize_query(uint256,string,string[5],uint256)", "0x971c803f": "getMinimumStackCheck()", "0x5168afa4": "getPackageHash(bytes,uint8,uint8,uint8)", "0xaef99eef": "Game()", "0x7f924c4e": "testDeposit()", "0xb1adc241": "BalanceDB()", "0x6a704d7b": "AddedToGeneration(address,uint256)", "0xb9f37c86": "Registrar()", "0xc631b292": "closeVoting()", "0x19350aea": "nameFor(address)", "0x85dee34c": "query2_withGasLimit(uint256,string,string,string,uint256)", "0xbf187478": "shift_left(uint64,uint256)", "0x5b6b431d": "Withdraw(uint256)", "0xe5dd90a5": "HumanStandardToken(uint256,string,uint8,string)", "0xbf8c50ff": "scheduleTransaction()", "0x013d64bd": "setCanCall(address,address,string,bool)", "0xb870ecbb": "testNormalWhitelistAdd()", "0xbcd3d8ca": "Collector(address,address,uint256)", "0x4401ff5c": "sellShares(bytes,uint8,uint256,uint256)", "0x3e0dfbdf": "getInvestorByAddress(address)", "0xcb3e64fd": "unhalt()", "0xafc24e3d": "getChallengeAnswer(uint256)", "0xa36c8ec2": "UpdateContractorAddress(address)", "0xd5a4a3c6": "findRecentBet(address)", "0xb028ee13": "s2b(string)", "0x692ad3a9": "round(uint256,uint256,uint256,uint256)", "0x0a7493b4": "Etheropt(uint256,string,uint256,uint256,bytes,address,int256[])", "0x60689557": "Rock()", "0x717fedf0": "getFirstActiveDuel1()", "0xe837ab59": "getParticipantByAddress(address)", "0x32d5fe98": "revealCampaign(uint256,uint256)", "0x0178b8bf": "resolver(bytes32)", "0x44faa139": "Withdraw(uint32)", "0x418cf199": "setEstimateCost(uint256,uint256)", "0xae45850b": "schedulerAddress()", "0xd4871517": "BTCLotto(address,uint256)", "0xfbffb355": "testBitsEqualFailIndexOOB()", "0x2be6d43c": "ARKTagger_1_00()", "0x1df47aad": "ReplayProtection()", "0xb36a0b15": "getSignDetails(uint256,uint8)", "0x9c4baf27": "Skywalker(address,address)", "0xfb34fc6f": "WatchNextBlockReward()", "0xceba30b5": "scheduleTransaction(address,bytes,uint256[4],uint256)", "0xfc94dd18": "verifyHumanStandardToken(address)", "0x1ff13086": "size(int256)", "0xce92dced": "newBid(bytes32)", "0x231944e2": "moveUnits(uint256,uint256,uint256[])", "0x784813e0": "lookupBet(uint256,uint256)", "0xe0e3ba5a": "getLosesShare(address)", "0x5c1b3ca1": "getConfigUint(int256,bytes32)", "0x91060168": "fetchString(address,bytes4,bytes32)", "0x354b2735": "testDeploy()", "0xb88eef53": "registryCreated()", "0x8e3d4e5e": "Fibonacci(bytes)", "0x64bd87d6": "scheduleCall(address,bytes,bytes,uint256,uint256)", "0x5c3d005d": "demote(address)", "0xb6509c12": "Ethereum_twelve_bagger()", "0xe87508be": "investorDeposit()", "0xcb96012e": "hashTo256(bytes32)", "0x314e0fb6": "scheduleTransaction(address,bytes,uint256[3],uint256)", "0x5b6a54bc": "adjustTransactionFee(uint256)", "0x5f515226": "checkBalance(address)", "0x76cd7cbc": "sign(bytes)", "0xce220ecf": "testAddBalanceFailsAboveOverflow()", "0x922fc84b": "taskProcessedNoCosting(uint256)", "0xf8b2cb4f": "getBalance(address)", "0x7a29332d": "buyAllOutcomes(uint256,uint256)", "0x04106c8b": "startGeneration()", "0x8eaa1e29": "getContentByData(address,uint256,string,string)", "0x5a7a8850": "rollWithSeed(bytes32)", "0x2facc4e8": "depositGovernance(uint256,address)", "0xf2561a43": "voteSuicide(address)", "0xee1b4828": "closeBooks()", "0x18f303a1": "SetInternalValues(uint8,uint256)", "0xce60f78d": "createMarriage(bytes,bytes,uint256,bytes,bytes)", "0x18b749c4": "payEther(uint256)", "0xbbc6eb1f": "getDefaultDonation()", "0x0b1ca49a": "removeMember(address)", "0xccf4f413": "setSubRegistrar(string,address)", "0x4616caa9": "pushCoin(uint256,address,string)", "0xd69450d5": "setUUID4Bytes(bytes)", "0xc98165b6": "createTarget()", "0x8c79a24d": "refName(uint256)", "0x56d88e27": "len()", "0xbfc3d84b": "CT()", "0xc7f2e6af": "Contribute(bytes20)", "0xd7e11e9d": "AddTicket(bytes)", "0x2d06177a": "addManager(address)", "0xd588acc4": "claimMiningReward()", "0x2ffb9e64": "updateGasForXaurData(uint256,uint256)", "0xf896503a": "getConfigAddress(bytes32)", "0x9af8c4ba": "respond(uint256,address,bytes)", "0x82a5285d": "getMinBetAmount()", "0x3ead67b5": "changeContractOwner(address)", "0xb29d7914": "getRefResults(uint256)", "0x135128c2": "CounterPartyDeposit()", "0x795b9a6f": "scheduleCall(address,bytes4,uint256,bytes)", "0x83197ef0": "destroy()", "0x433836dc": "scheduleTransaction(address,bytes,uint8,uint256[3],uint256)", "0xf009347d": "KudosProxy(address)", "0xf2fde38b": "transferOwnership(address)", "0x62c99e84": "_Approval(address,address,bytes32)", "0x1b83b823": "notifyPlayer(uint256)", "0xe56c8552": "spinTheWheel(address)", "0x93eec1fb": "setName(uint8,uint8,string)", "0x11af3c68": "divest(address)", "0x8279c7db": "setReceiverAddress(address)", "0x44691f7e": "hasStarted()", "0x349501b7": "checkDepth(uint256)", "0xe8beef5b": "fireEventLog3Anonym()", "0x0870607b": "addSubUser(address)", "0x063925c8": "scheduleCall(bytes,uint256,uint256)", "0xa23744f0": "tryCreateCheckRecord(bytes)", "0x35d79fad": "CertificationDb(address,uint256,address)", "0x44691f2b": "Dispute()", "0xd7ccc2c3": "getLastPayment()", "0x152583de": "getAttributes()", "0x1a9360dd": "checkDate()", "0x420ef2b3": "TargetHash()", "0xf0caea2b": "SmartRoulette()", "0x5d268629": "Refund()", "0x23385089": "emitApprove(address,address,uint256)", "0x7648c929": "returnRemainingEther()", "0x5d5bc4cb": "BetOnRed()", "0xde8fa431": "getSize()", "0xda6b31b9": "testErrorTransferToNullAuthority()", "0x444dd6f3": "Elcoin()", "0xe1fa8e84": "register(bytes32)", "0x3e0a322d": "setStartTime(uint256)", "0x21bacf28": "getDefaultFee()", "0xb1662d58": "setModule(address,bool)", "0x5b0fc9c3": "setOwner(bytes32,address)", "0x40193d17": "getPongvalConstant()", "0xfb95adeb": "testFailBlockhashInsuffiecientFee()", "0x8ecc0950": "returnToOwner()", "0x34b7ac9b": "END_MINTING()", "0xeaa37394": "create(bytes,bytes32,bool,bool,bool,bool,bool)", "0xd7bc23af": "newParameters(int256,uint256,uint256,uint256)", "0x97709cde": "ARK_VOTER_1_00(uint256,uint256,uint256,uint256,uint256,uint256)", "0xcf4a1612": "scheduleTransaction(uint256,address,bytes,uint256)", "0x40695625": "testRetractLatestRevision()", "0x90c3f38f": "setDescription(string)", "0x5fdf05d7": "two()", "0xaaac50bd": "transferDisable(bytes32)", "0x206a44f3": "getNum(bytes,uint256)", "0x7ac26aeb": "getTag(string,uint256)", "0xdc3f65d3": "createdByMe()", "0xe99543aa": "Trash(uint256)", "0x1bcad37a": "getTotalCost()", "0xa7f43779": "remove()", "0x3416f9d4": "subtractSafely(uint256,uint256)", "0x3bcf7d22": "newBribedCitizen(address)", "0x918f1bb5": "ProjectKudos()", "0xcf09e6e1": "SetBigContract(address)", "0x23add736": "claim(uint256,uint256,uint8,bytes,bytes)", "0x8ac6a869": "isObsolete()", "0x2324c67c": "getAllSignatureHashes(bytes4)", "0x981a60f5": "extractNameFromData(bytes)", "0xc018d0e6": "getFeeAmount(int256,int256)", "0x50e06b57": "Etherization()", "0x49e65440": "setSymbol(bytes32)", "0xfaee13b9": "set(int8)", "0x01ffc9a7": "supportsInterface(bytes4)", "0xc0b92612": "changePig(address)", "0x3f9f5b68": "setPreviousID(uint256,int256)", "0x2e9c5e77": "doStackExtension(uint256)", "0xc83be888": "single_move(uint256,uint8,uint8)", "0xef19c332": "_checkSigned(bytes32,uint256,uint8,bytes32,bytes32)", "0x2f30c6f6": "set(uint256,address)", "0x62986e27": "Canary(address,uint16)", "0x44dfdce0": "getNameOwner(bytes)", "0x4b7fcee7": "ownerPausePayouts(bool)", "0x84dac46e": "Fucksign()", "0x0878bc51": "getAttachesto(uint8)", "0x42d16748": "getMinDailyWithdrawalLimit()", "0x4ecd73e2": "DistributeDividends(uint256)", "0x03750d94": "serverSeed(address,bytes32)", "0xea0a5237": "announce(string)", "0x611daa7e": "EmergencyBalanceReset(uint256)", "0x2ae87a70": "getNumContents(address,uint256)", "0x5ec01e4d": "random()", "0xfd735602": "executeN()", "0xfebefd61": "startAuctionsAndBid(bytes32[],bytes32)", "0x8b64d70e": "owner_set_time_limit(uint256)", "0x2839e928": "ackermann(uint256,uint256)", "0xaf29e720": "remainingGasFund(uint256)", "0x9eee85fe": "bookEarnings(address,uint256)", "0x3733ffca": "convertTo(uint256,string,string)", "0xdbde1988": "transferFromWithoutReward(address,address,uint256)", "0xd3edcb5b": "getCreditorAddresses()", "0x77fcb91d": "forward(address,bool)", "0xf062e26b": "check_darkdao()", "0xdef2489b": "convert(address)", "0xccbda1af": "getChannelByName(string)", "0x267c8507": "authorizeManager(address)", "0xf1c30ec0": "reclaim(bytes)", "0xe6470fbe": "updateDefaultPayment()", "0x8fe58eb9": "Triger()", "0xd6b44859": "scheduleUndoIt(uint256)", "0x0eb8ed07": "transferEnable(bytes32)", "0xfc1f2a70": "Add(uint256,string,string)", "0x058026d0": "checkTransferToICAPWithReference(bytes32,uint256,string)", "0xaf27c7b3": "Security_HasPasswordSha3HashBeenAddedToBankAccount()", "0x5b151fd2": "fifty_fifty()", "0x531d1974": "testThrowRetractLatestRevisionEnforceRevisions()", "0x60213b88": "getInitialWithdrawal()", "0x29e94503": "VersionedBlob()", "0x9c5d7030": "reimburseGas(uint256,address,uint256,uint256)", "0xcd9a3c98": "any(bool[7])", "0xb484e532": "getMyMsg()", "0xd1f59db9": "isLatestMinorTree(bytes32,bytes32)", "0xe0c6190d": "checkTime()", "0xaf5610dd": "isThisPreforkVersion()", "0xfa968eea": "minBetAmount()", "0x003538c5": "TestRegistrar(address,bytes32)", "0x71c59097": "MainnetSurvey(uint256,string,bytes32[])", "0xc82aac47": "searchByTag(bytes32)", "0xeca5c793": "testErrorUnauthorizedNameRegister()", "0xdc75f2db": "multiowned(address[],uint256)", "0x4c9ed763": "requestTokensBack()", "0xbd9a5673": "oraclize_query(string,string[5])", "0xb11e3b82": "createEvent(bytes32,bool,int256,int256,uint8,address,address,bytes32[])", "0x86e4e178": "CheckTickets(address,uint256,uint256)", "0x53c84526": "setSmartAffiliateContract(address)", "0x89029d8c": "get_all(uint256,uint256)", "0xc25e6908": "ultimateOutcomes(bytes32)", "0x6b1e564a": "challengeWinningOutcome(bytes32,uint16)", "0x1c895915": "getNumberOfPayments(uint256)", "0x244fcd03": "removeRelease(bytes32,string)", "0x2ca6d2c0": "getAccountSize(address)", "0xfe67a54b": "endAuction()", "0x756fb8c9": "getOptionChain()", "0x88102583": "safeCastSigned(uint256)", "0x60063887": "transferDebt(address,address,address,uint256)", "0x16216f39": "return13()", "0x8089d001": "getHashOfBlock(uint256)", "0x27bc39c0": "submitCanonicalCandidate(bytes,bytes,bytes,bytes)", "0xb29ae23f": "getDateOfSignature()", "0xdd729530": "add_shield(uint16)", "0x38557648": "executeSellOrder(address)", "0xe2a71f12": "accountDelete()", "0xce869a64": "fails()", "0x69bdfd3a": "toContractDie(bytes,bytes,uint256)", "0x99aeade3": "iterateTable(uint256,uint256)", "0x46bdca9a": "equal(string,string)", "0x5e07f240": "shiftBitsLeft(bytes,uint256)", "0x76285b5b": "_is360thDay()", "0x594151e0": "Dice()", "0x4dc43eaf": "setTreasury(uint256,uint256)", "0xec727000": "getApprovalDB()", "0xb144adfb": "balance_of(address)", "0x63052d82": "getOwnersIndex(address)", "0x833b4596": "testApproveSetsAllowance()", "0xc67d376d": "getClosedCandidates()", "0xcf03f5f4": "activateMasterKey(address)", "0x7ca55e00": "etherandomVerify(bytes32,bytes32,bytes32,uint256,uint256)", "0xd92ebe46": "createDAO(address,uint256,uint256,uint256,string,string,uint8)", "0x3773930e": "ConfigureFunction(address,uint256,uint16,uint16,uint16)", "0xfee6d28c": "addSnapshot(string)", "0xe71264fa": "addNewTokens(uint256)", "0xc4d9102f": "setNextID(uint256,int256)", "0xa6f0e577": "isLeapYear(uint16)", "0xc7102df7": "__stopBlock()", "0xfa93019c": "getBlocks(uint8,uint8)", "0xe9794dc1": "CreateHash(uint8,string)", "0x5460ef10": "sendWithExtraGas(address,uint256,uint256)", "0x2f62a6ff": "fipsRegister(uint256,address,bytes)", "0xd630bd53": "pledgeApprove(uint256)", "0x3448c7d6": "createHistory(bytes,address,address)", "0xdda3342b": "ReplicatorFactory()", "0x0cd865ec": "recover(address)", "0xb9f28076": "historyIdx(address)", "0x4dda1764": "CafeMaker()", "0x883ba26b": "getIsSettled()", "0x3f5e268f": "convictInitial(uint256,uint256)", "0x4a3b0eec": "authorizeOpen(uint256,bool,string)", "0xee6d2641": "sendWithExtraGasExt(address,uint256,uint256)", "0xde14bbf7": "randomGen(uint256,uint256)", "0x7f3bd56e": "disburse(address,uint256)", "0x20d8741f": "Feed()", "0x60c6b3a5": "claim(bytes,address,uint256,uint8,bytes,bytes)", "0xda4b5e29": "contains()", "0xb3c25835": "addUser(address,string,string,uint256)", "0xee2af3fb": "set_factory(address)", "0xb821f815": "pay_winner(uint256)", "0x138cc941": "testErrorTransferToRejectAuthority()", "0xc0b6f0c2": "NextRoundAndEvents()", "0xc7e67360": "GAS_BUFFER()", "0x058d7433": "setAlliesContract(address)", "0xd810f298": "computeSettlementAmount()", "0xa24d23eb": "ProcessGame(uint256,uint256)", "0x7ac91cc2": "testFailOwnedAuth()", "0x79c3ddc1": "isPackageOwner(string,address,address)", "0x478ae93c": "playToWin(uint256)", "0x6632a507": "testSetupPrecondition()", "0xb6013cef": "finalize(uint256,uint256)", "0x37b7bf11": "Tile(int256,int256)", "0xecfc7ecc": "placeBid()", "0x70b1d9d4": "requestCanonicalFormat(bytes)", "0x315fdea3": "TreasureChest()", "0xc5575ef0": "checkTransferFrom(address,address,uint256)", "0x65c72840": "getDay(uint256)", "0xd6eafd08": "scheduleCall(address,bytes,bytes,uint8,uint256[4])", "0x350fbe2e": "calcNextDrawTime()", "0x8af784dc": "expectEventsExact(address)", "0x2db89533": "Auth(uint8,address)", "0x9f203255": "setAuditor(address)", "0x2526d960": "clawback()", "0x3fbd40fd": "ProcessDraw()", "0xface030b": "SpinTheWheel(address)", "0x648621ec": "xnotify(string)", "0x22dc36e2": "processed(uint64)", "0x6f52167d": "payDuel(address,string,address,string)", "0x8f70bfa0": "processDeposit()", "0x25ea269e": "Scissors()", "0x93feb13b": "ForceSendHelper(address)", "0xb688a363": "join()", "0x89859b50": "updateLatestTree(bytes32)", "0xf83d08ba": "lock()", "0x7d287697": "testTryGetUnset()", "0x98d5fdca": "getPrice()", "0xfe72e717": "toDie(bytes)", "0xb3c06f50": "transferFrom(address,address,bytes32)", "0x1465aa97": "testingContract()", "0x069d6d1c": "closeOrder(uint256)", "0xa79f26dc": "force()", "0xf2371fb3": "grantGiveableKudos(address,uint256)", "0xaa7dcd84": "testUpdateAuthorityEvent()", "0x0d8b5fa2": "testControllerValidTransferFrom()", "0x0e0f55d0": "RewardOrder(uint256,uint256)", "0x9ea1b79d": "getContentChannel(uint256)", "0x4a67fa7d": "setLotteryFee(uint256)", "0xdb006a75": "redeem(uint256)", "0x8f4ed333": "step2()", "0x1a10cfc3": "delete_entry(uint256,uint256,uint256)", "0xd422e4e0": "takeFee(address,uint256,string)", "0x61a00f6d": "Ballot(bytes32[])", "0x9c30936f": "removeCertificationDocumentFromSelf(bytes32)", "0xa5f4af33": "playerWithdrawPendingTransactions()", "0x07ad9ecb": "safeSend(address,uint256)", "0x8f99ea43": "setDividendDB(address)", "0x1df473bc": "newContract(bytes)", "0xea5ea470": "payFunding(uint256)", "0x743e0c9b": "receiveTokens(uint256)", "0x21835af6": "__dig(uint256)", "0x47448e8a": "set(bytes32,string,bytes32)", "0x9b1ad792": "destroyToken(address,uint256)", "0xf765088f": "UpdateClientAddress(address)", "0xddbbc35c": "searchByName(string)", "0x5ed7ca5b": "halt()", "0x97950740": "roomForBirth()", "0xfc01abbe": "stringToBytes32(string,string)", "0xea3d508a": "selector()", "0x8c88752a": "ContributorList(uint256)", "0x5837e083": "move_history(uint256)", "0xf7c3ee7a": "immortality()", "0x1b9f9647": "accessMyWallet(address)", "0xc8691b2a": "getHistory(uint256)", "0x91e8d3dc": "testBitOrFailIndexOOB()", "0x5c89c10d": "setBannedCycles(uint256[])", "0x4500054f": "isCancellable()", "0x334ef224": "testThrowsUpdateLatestRevisionNotOwner()", "0x763a738c": "allNames()", "0x45590ec8": "addTag(uint256,string)", "0xe7740cf9": "revealPaper(string)", "0xd9d2d058": "Splitter()", "0xb412d4d6": "CafeDelivered()", "0x8365172c": "num_levels()", "0x41c0e1b5": "kill()", "0x3106fea0": "voteOnProposal(uint256,bool,uint256)", "0x82ab890a": "update(uint256)", "0x4636a159": "newPhoneToAddr(address,uint256)", "0x2f29d8c5": "elapsed()", "0x1bf20668": "testAdminTransfer()", "0xf709dd51": "getTrademark()", "0x8b859409": "setRelease(bytes32,bytes32,string)", "0x03959bb7": "setDataContract(address)", "0x4247f52d": "DoRoll()", "0x31ab4066": "testAuthorityTryAuth()", "0xac4b2bae": "newParameters(int256,uint256,int256,uint256)", "0x57eaeddf": "_isContract()", "0x4a3a87e2": "CreateProxyWithControllerAndRecoveryKey(address,address,uint256,uint256)", "0xd116c8c4": "releasePayment()", "0x6615dd83": "setSeedSourceB(address)", "0xb8aca90b": "CurrentGame()", "0xc124e2ea": "checkBetDozen(uint8)", "0x4b0bbf84": "addEntropy()", "0x452fbc41": "USN(address,address,bytes,uint256,uint256,uint128)", "0xcdb6753b": "setNav(uint32)", "0xbb5d40eb": "isValid()", "0xd6f42038": "PhoneToAddress()", "0x6bc3e0f0": "verifySecondHalf(uint256[4],uint256[4],uint256[4])", "0x33893071": "checkMyWithdraw(address)", "0xfb46d4c5": "tweet(string)", "0x248582b0": "receivePaymentForGoodsSoldEarly()", "0x766a3f2e": "Security_ConnectBankAccountToNewOwnerAddress(uint32,string)", "0x1c8d5d38": "allowance(address,address,bytes32)", "0x6b256f57": "DAOSecurity(address,address,bytes,uint256,uint256,uint128)", "0xe8d1e961": "lockAccount(uint256)", "0x152fb125": "SimpleMixer()", "0xf72457af": "CertifierDb()", "0xe8a5282d": "setConfig(bytes32)", "0xbeb92f55": "setCaller(address)", "0x9a571d9f": "isAlphaLower(bytes1)", "0x46a2679a": "getSubpotsCount(uint256)", "0xd62d3115": "testCreate()", "0xb6ed0632": "cancelOrder(uint256,uint256)", "0xc95e81cb": "MyBet(uint8,address)", "0x1d5a9f3f": "object_types(uint256)", "0xa49d53a1": "SmartRevshare()", "0x5b65b9ab": "setFee(uint256,uint256,uint256)", "0x116c6eab": "getProfitShare(address)", "0x8e46afa9": "getDefaultGracePeriod()", "0xdabc706e": "getProposalCost()", "0x3fbb539d": "scheduleCall(address,bytes,uint256,bytes)", "0x86269a88": "checkBetNumber(uint8)", "0xb6ac24df": "updatePatchTree(bytes32)", "0x4637d827": "trust(address)", "0x1c1b8772": "update(address)", "0x5a9f2def": "scheduleCall(bytes4,bytes,uint256,uint256)", "0x81a60c0d": "getResults(uint256)", "0xd1b4ff7e": "multiAccessRevokeD(bytes32,address)", "0x92b7d5b9": "getCurrentGaslimit()", "0x77ceded8": "mintGrey(int256,address,uint256)", "0x2a095fbe": "unlinkEID(bytes,bytes,address)", "0xa6e16ba2": "testThrowsRetractLatestRevisionNotOwner()", "0x4579268a": "getOffer(uint256)", "0xcabb3a3a": "isAlphaNumeric(string)", "0xfadc51cf": "isAlpha(bytes1)", "0xf2022905": "toldYouItWouldWork()", "0x686e8aaa": "GetMoney()", "0x07718a3b": "BankOwner_WithdrawDonations()", "0xc58343ef": "getRequest(uint256)", "0x7b1a547c": "registerAs(address,string,uint256,string,address)", "0x213b9eb8": "setAddr(string,address)", "0x75090ebf": "changeDomain(uint256,uint256,uint256,address)", "0xdbbdf083": "register(uint256,address)", "0xfa4e5e5a": "notify(uint8,string,string)", "0x86a5ff97": "changeStatus(string)", "0xb8f71f26": "scheduleTransaction(uint256,address)", "0xa2ec191a": "addDSource(string,uint256)", "0x18b31f94": "registerLengthFunction(string,string,address)", "0x7b395487": "voteForUltimateOutcome(bytes32,uint16)", "0x39246d75": "VersionModel()", "0xd500dd6a": "challengeTimeout(uint256,bool,address)", "0xd1da09ee": "extractImportFeeChargeLength()", "0xc74e907b": "commit(address,uint256,uint256)", "0x4b09ebb2": "e_exp(uint256)", "0xec3af4a9": "getProjectKudos(address)", "0x714064f3": "BreakableBond(address,address,uint256)", "0xc4bc5da5": "resumeContract()", "0xf7888aec": "balanceOf(address,address)", "0x2f597e71": "testLongInput()", "0x7212b67e": "add_potion(uint16)", "0x9a15f4f3": "getBlockHeader(int256,int256)", "0x6eacd48a": "ownerPauseGame(bool)", "0xf739ed4c": "id_for_user_version(uint256,uint256)", "0xfaf0952b": "testThrowRestartNotOwner()", "0x88a1e895": "test2Fails()", "0x237e9492": "executeProposal(uint256,bytes)", "0x7cb97b2b": "set_owner(address)", "0x2bb685bc": "kill2()", "0xdc52696f": "tokenSupplyChanged()", "0x83d852d9": "shutdownTransactions()", "0x525b25b1": "getDeploymentReward()", "0xeac116c4": "createKingdom(string,address,address,address,address)", "0x014e5fde": "ARKController_1_00()", "0xc6ae3b57": "dEthereumlotteryNet(address,address)", "0xcddbe729": "game(uint256)", "0x8823a9c0": "changeFeeTake(uint256)", "0x021991e7": "getBetsLocked()", "0x3015394c": "cancelRequest(uint256)", "0x9d118770": "destroy(uint256)", "0xe854dfb4": "Order(address,uint256,uint256)", "0x8435be4b": "getLastFarm(uint8,uint8)", "0x27fbcac5": "getChannelFeed(address,uint256,uint256)", "0xc1be4031": "XaurumProxyERC20()", "0x8e25071a": "setProxyCurrator(address)", "0x4f139314": "compensateLatestMonarch(uint256)", "0x85c78fac": "retryOraclizeRequest(uint256)", "0x478e25bf": "resetAction(bytes32)", "0xc74c251f": "addSafely(uint256,uint256)", "0x058aace1": "divest()", "0x6d1da953": "createWithNonce(bytes32,bytes)", "0x30c0f8d6": "scheduleTransaction(address,bytes)", "0x69a5e902": "multiAccessCall(address,uint256,bytes)", "0x6f8b44b0": "setMaxSupply(uint256)", "0x919edc7c": "getChainySender(string)", "0x0b7ad54c": "getContent(uint256)", "0x5bfdc700": "registerData(address,int256,bytes,address)", "0x0d1fce42": "getBankroll()", "0x739b47ca": "recordWin(address)", "0xa5ea11da": "getParameters()", "0xf8af9e6f": "setAdv(uint256,string,string)", "0xe32e9f22": "setDeploymentReward(uint256)", "0x0baaaed9": "setConfigBytes(bytes,bytes)", "0x99f4b251": "mine()", "0x362af076": "createRequest(address[3],address,uint256[11],uint256,bytes)", "0x7fd238ba": "doCoinage(address[],uint256[],uint256,uint256,uint256)", "0x3adb2de7": "bet_this_spin()", "0xa311dd70": "setArray(uint8[10])", "0xc5bf339c": "getLastNonPublished()", "0x9d1bbd7e": "CancelRoundAndRefundAll(uint256)", "0x89790192": "WithFee(address,uint256)", "0x1c879c47": "getMarketHashes(bytes)", "0xbb84d362": "splitProfitVIP_only_Dev()", "0xffb1a6cb": "getWins(address)", "0x3b355af6": "baseData()", "0xb181a8fc": "resetContract()", "0x7d3d6522": "goalReached()", "0xd4c2b6b1": "scheduleTransaction(address,bytes,uint256[5],uint256)", "0xd65ab5f2": "startGame()", "0x4c4766e8": "KittenRegistry()", "0x77e5bf84": "getTxGasprice()", "0xff981099": "getVotes(uint256)", "0x4a7b26ec": "join_game(uint256)", "0xcccf7a8e": "has(uint256)", "0xa525f42c": "transferFromToICAP(address,bytes32,uint256)", "0xeef8e35f": "setChainyURL(string)", "0x557ed1ba": "getTime()", "0x595da94d": "has_owners(uint256)", "0x12511c14": "transferEnable(bytes20)", "0x2b291eb6": "UserAddTicket(bytes)", "0x50baa622": "withdrawToken(uint256)", "0xc01a8c84": "confirmTransaction(uint256)", "0x671dacdc": "CalculateSqrt(uint256)", "0xe74ffbd5": "getPart(bytes32,uint256)", "0xdd54a62f": "content(string)", "0x4025b293": "redeemAllOutcomes(bytes32,uint256)", "0xa8659216": "setInitialLockinDays(uint256)", "0x00b5b223": "computeResponse(uint256,uint16)", "0x2ef761d3": "buyTile(uint8,uint8)", "0x0a874df6": "lookup(uint256)", "0x42c69566": "get_address(address,string)", "0x02dc2e1d": "queuePayment(bytes)", "0x86bb7121": "getBlocksPerRound()", "0xacfdfd1c": "deploy(uint256,string,string,address)", "0x7d298ee3": "beforeExecute(address,uint256)", "0x5023d124": "TestFactory()", "0x827ef325": "_parseMsgData(bytes)", "0xd35b9d83": "codeAt(address)", "0x26161670": "donkeyRanking(uint256)", "0xe0834ea4": "WatchBalanceInEther()", "0xd44f2d3d": "getInitialWithdrawalDone()", "0x4f223fe3": "StatefulFactory(string,string,string)", "0x91cd242d": "setMeta(bytes32,bytes32,bytes32)", "0x9a97043b": "depositIdx(address)", "0x85db2dda": "PayoutQueueSize()", "0x423e1298": "setDoNotAutoRefundTo(bool)", "0xb7a97a2b": "isValidChannel(uint256)", "0xc1441172": "setBlackFlagRequest(uint256,uint256)", "0x53d9d910": "create(address[],uint256,uint256)", "0x64d905c0": "awaitingParticipants()", "0x718bd6dd": "setRequestUntil(uint8)", "0x5a353193": "KrakenPriceTicker()", "0xfb099c84": "newInvestor()", "0xd264e05e": "forward()", "0xcd9f05b8": "balanceEtherAddress(address)", "0xa1da2fb9": "retrieveDAOReward(bool)", "0x60708ae3": "issueAndCommit(address,address,uint256,uint256)", "0x109df68e": "rotateBitsRight(bytes,uint256)", "0x793cd71e": "cashOut()", "0xd3017193": "addUser(address,uint256)", "0xaacf5328": "setVideoID(string,uint256)", "0xb56e1bca": "setExchangeToken()", "0x9341231c": "sendOrThrow(address,uint256)", "0xaed8f3da": "partsPerBillion(uint256,uint256)", "0xdcff5581": "NewFeeAddress(address)", "0xbbe4fd50": "getNow()", "0x3df16377": "make_move_and_claim_victory(uint256,uint8,uint8,uint8,uint8,uint8,uint8,uint8)", "0x5ae5df8f": "deleteRef(string)", "0x6d853ab6": "isSubUser(address)", "0x28472c6c": "claimComputation(bytes,bytes)", "0x2c215998": "updateStatus(string)", "0x7eff1465": "setAccountAllowance(address,address,uint256)", "0xd5089396": "Token(string,string,uint8,uint256)", "0xd1f7a4e4": "createCertificate(bytes)", "0x8c0e2a31": "regProxy(address)", "0xa819819b": "sweepDeityCommission(uint256)", "0x2b861629": "storeBlockHeader(bytes)", "0x25d4bdeb": "LookAtCollectedFees()", "0x5dddea66": "updateState(uint256,uint8,uint256)", "0x3ccfd60b": "withdraw()", "0x6795dbcd": "getAddress(bytes32,string)", "0x9b9ba572": "oraclize_query(string,string[3])", "0xa925d85e": "Exchange(address,address)", "0xbfe8c107": "betOnDozen(bool,bool,bool)", "0x1af716ba": "transferFrom(address,address,uint256,string)", "0x67eae672": "sendCoinFrom(address,uint256,address)", "0x311d5a2a": "recordBalance(address)", "0x7ca823d5": "getAverageChainWork()", "0x19483cd1": "checkHash()", "0xd366fbab": "startLottery(bytes32,uint256,uint256,uint256,uint256,bool)", "0x4d70d1d7": "generateId(uint256)", "0xe13dc28b": "testValidTransfers()", "0x12065fe0": "getBalance()", "0xdd67a360": "OrderLifeCycle()", "0xd7c23572": "historyTimesPlayed(address)", "0x2675c123": "CloseContract()", "0x1381e400": "cancel(uint32)", "0xa48a663c": "transferFromToICAPWithReference(address,bytes32,uint256,string)", "0xb03260be": "scheduleTransaction(uint256,address,bytes)", "0xb37217a4": "getRandomNumber(uint256)", "0x5c54305e": "InsufficientFunds(address,uint256,uint256)", "0x17e1b09b": "minimumDeposit(uint256)", "0x10c4e8b0": "all()", "0xa31d5580": "Registrar(address,bytes32,address)", "0xbe6307c8": "getDraw(uint256)", "0xc985c221": "get_all_levels()", "0x91b7f5ed": "setPrice(uint256)", "0xe42def21": "CryptoHill()", "0x738486bd": "BeerCoin(uint256)", "0xe422ebe9": "getBot()", "0x67dd74ca": "buyTicket(uint256)", "0x276b94e1": "copypaste()", "0x39aaba25": "get_status()", "0x7ed19af9": "multiAccessRevoke(bytes32)", "0x4c1b2446": "transmitInteger(address,bytes,bytes,uint256,uint16)", "0xd014c01f": "enter(address)", "0x1d49e081": "EXECUTE_EXTRA_GAS()", "0x9dafbc13": "initBlock(uint256)", "0xc7e22ac4": "setOracleGas(uint256)", "0xa3053236": "SafeInvestments()", "0xf42ac1de": "minQuorum(uint256)", "0x04d91c6a": "testFail()", "0x0e662cf0": "buyTokens(uint16)", "0x1ef0625b": "player_2(uint256)", "0xcec1365a": "ShortLimit(uint256)", "0x340f5e4e": "get_all_num_levels()", "0x3e2729bf": "isRevocated(bytes)", "0x5a1cc358": "getChannelRank(address,uint256)", "0x4d366398": "runPeerBalance()", "0xaf9a3f9b": "hashName(string)", "0x33298e25": "invoke(uint256,uint256)", "0x63def590": "untrustClient(address)", "0x836d6d66": "WeeklyLotteryB(address,uint256)", "0x7f497550": "scheduleTransfer(address,uint256,uint256)", "0xf9e05ed9": "sha(uint128)", "0xf6458c6a": "toZ1(uint256[3],uint256)", "0xf41017fc": "finalize(uint24)", "0xeeb57139": "CollectMoney(uint256)", "0xfdacd576": "setCompleted(uint256)", "0xb7266456": "StandardToken()", "0x6a8c2437": "totalRescues()", "0x1fdf6e0c": "protectKingdom()", "0xcf31e9fe": "getOutputHash()", "0xc8117b5b": "extractBalanceOfLength()", "0x0674763c": "assert(bool)", "0x87def081": "getFeeRecipient(int256)", "0xc63ff8dd": "claim(bytes)", "0x329bfc33": "getCurrentWinner()", "0x3d6a3664": "setNewOracle(address)", "0xdaa21e0e": "testBitSetSuccess()", "0xbf8783e0": "callAndGetReturn(address,bytes,uint256)", "0xb06df18e": "transfer(bytes20,address)", "0x00100a18": "NewPoll(string,string,uint256,uint256)", "0x2ffb8631": "getReleaseLockfileURI(bytes32)", "0x6716a692": "setDVIP(address)", "0xe8223468": "sha3clone(bytes)", "0x0aeacb5e": "getTotalRecords()", "0x29e30910": "testThrowCreateExistingNonce()", "0x240ecad5": "transferViaProxy(address,address,uint256)", "0xa33d4968": "Tripler()", "0x8caaaae6": "totalWeiPrice()", "0xf28a7912": "quick2()", "0xcbe9ef39": "BasicCoin(uint256,address)", "0xea3d2827": "selectWinner(string)", "0x92e9fd5e": "ColdWallet(address,address)", "0xcd5e3c5d": "roll()", "0x4a82534b": "create(address,address,address,uint256,uint8,uint8,uint256)", "0x5ccd2f9b": "_deleteAllPackedRevisionBlockNumbers(bytes20)", "0x9380b8e7": "testFailAddingMembers()", "0x31b0795c": "registerAddress(address,address)", "0xb76e4890": "Tester()", "0x29c08ba2": "payPremium()", "0xd7f31eb9": "forward(address,uint256,bytes)", "0xd7ef1356": "best_adjustment(bool)", "0x48d9a374": "blockTransfer(address,uint256)", "0x88b9e10e": "seizeTokens(address,uint256)", "0x8736fd16": "getRefStatus(uint256)", "0x2b30d2b8": "invoke(uint256)", "0xd4e78272": "Draw()", "0x0257c48c": "meta(bytes32,bytes32)", "0xc1246d39": "simulatePathwayFromBeneficiary()", "0x699b328a": "randomize()", "0xa200dc73": "getNextShareholder(address)", "0x9a7a7c11": "makeRoll(uint256)", "0x2bf1f9da": "restart(bytes32,bytes)", "0x943a32bc": "Relay(address)", "0x93503337": "isAllowed(bytes32,uint256)", "0xe97b2190": "add_wall(uint16)", "0x0448f79f": "addOptionChain(uint256,string,uint256,uint256,bytes,address,int256[])", "0xc0df77d0": "getRefName(uint256)", "0x27121069": "verify(bytes,uint8,bytes,bytes)", "0xb7009613": "canCall(address,address,bytes4)", "0x0295d71b": "currentDepositLimit()", "0x35ee2783": "Alarm()", "0x71b6663e": "play1(address,uint256)", "0x0178fe3f": "getData(uint256)", "0x489306eb": "oraclize_query(string,string)", "0xfce59d0c": "MangoRepo()", "0x8efc777f": "isBeta(bytes)", "0x1f7b8622": "getVotingDeadline()", "0x76da5667": "admin_kill()", "0x152cf9db": "getDataPoint(int256,uint256,uint256)", "0xd09de08a": "increment()", "0x64ee49fe": "scheduleCall(address,uint256,bytes4,uint256,uint256,uint8)", "0xd9fe60f3": "DTHPool(address,address,uint256,string,string,string)", "0x6b4dd158": "getPrice(bytes)", "0x0a80ef45": "getIsClosed()", "0x51fdaf92": "checkExpiredfunds()", "0x694e0d5b": "StringPasser(uint8[])", "0x0e1d88fc": "addTender(uint256,uint256,address,uint256)", "0x53850db3": "getParticipantById(uint256)", "0xb6cb405b": "getContractor()", "0x4c6b25b1": "results(bytes32)", "0x1c4e6cd0": "NameReg()", "0x53aab434": "buyIn()", "0x1ebe5c0f": "sendWithAllOurGasExcept(address,uint256,uint256)", "0xd98b9bb5": "placeBid(address,uint256)", "0x02e8d8c0": "scheduleTransaction(address,uint256,uint256)", "0x8a120dc9": "testBitEqualFailIndexOOB()", "0x33f707d1": "ownerWithdraw(uint256)", "0x98e00e54": "getCallWindowSize()", "0x4da74ee6": "setVoteIntention(uint256,bool,bool,string)", "0x6617e11a": "NiceGuyTax()", "0xfe13a823": "computeResponseFirstHalf(uint16)", "0xf7bd2361": "LookAtBalance()", "0xb09bc3bf": "try_to_get()", "0x0cee22e9": "testSetBalanceSetsSupply()", "0xae404996": "oraclize_query(string,string[3],uint256)", "0x2ad95786": "winner(address)", "0xe5fe4f31": "buy(uint8,bytes32,bytes32)", "0xe23941bc": "testDepositWithdraw()", "0xfc89aff6": "submitVerifiedUsers(address[])", "0xddf187b0": "dogFight()", "0xb5f5962a": "CALL_GAS_CEILING(uint256)", "0x92093dd6": "getLastResult()", "0xbfad16f4": "new_offer(uint256,uint256)", "0x01df7f30": "validateProposedThroneConfig(uint256,uint256,uint256,uint256)", "0x4054f5de": "EthVentures3()", "0x244c23ee": "Token(uint256,string,uint8,string)", "0xc3daab96": "withdrawBond(uint256)", "0x5fa21f1f": "enableBetting()", "0x3b591ea7": "AmountToForgeTheNextBlock()", "0x8c3c4b34": "getSaleStatus()", "0x7429f1eb": "multiAccessSetRecipientD(address,address)", "0xf99ff4df": "paged(uint256,uint256)", "0x2a64fb63": "getSaleDate(bytes)", "0x749f9889": "changeAllowedRecipients(address,bool)", "0x053c351b": "oraclize_getPrice(string)", "0x19663f7f": "TransferAmountFromBankAccountToAddress(uint256,address)", "0x5292c1a9": "testThrowsRestartEnforceRevisions()", "0x68f2ab8e": "Currency(string,string)", "0xd6e0bf29": "OwnerDeposit()", "0x94c3fa2e": "getLastBlockHashUsed()", "0x45362978": "query1(string,string)", "0xaff21c65": "getMinimumEndowment(uint256)", "0xe33c7ae2": "scheduleTransaction(uint256,uint256,bytes)", "0x9eb9dd3b": "getBetsProcessed()", "0x3807ba1b": "poi()", "0x7281854d": "GetCategoryValue(uint8)", "0x45ee49b9": "getUltimateOutcomes(bytes)", "0x0109f22e": "CrowdSale()", "0x98596726": "note(uint224)", "0x06900c41": "ZeroPonzi()", "0x3df76482": "fipsPublishData(bytes20,bytes)", "0xe0429b6c": "ShinySquirrels()", "0xa4a7cf5c": "redeemWinnings(bytes32)", "0x2e898ddc": "validateTemporalUnit(uint256)", "0x3af75ee1": "storeBlockWithFee(bytes,int256,bytes,int256)", "0x43e6125d": "Badge(address)", "0x75a6a332": "testThrowRetractNotRetractable()", "0xbed411a0": "CheckPrize(address)", "0x16f3cb5e": "__kill()", "0xe8efc1a0": "updatedValue(bytes32)", "0x9c7264d7": "fillOrder(address,uint256)", "0x9a0af2ec": "getStLength()", "0xf62cce34": "_clearRecordHierarchy(uint256,bytes32[],bytes32)", "0x940f851c": "Ballot(uint8)", "0xd96e5565": "testThrowsRetractNotRetractable()", "0x3a314b24": "SendETH(address)", "0xbd8c1d33": "checkTransferFromToICAPWithReference(address,bytes32,uint256,string)", "0x01da73ff": "isValidChannel(bytes)", "0x8f8bde82": "MicroDAO()", "0x2973e372": "isAlphaUpper(bytes1)", "0x1d2b7155": "activateImportFeeChargeRecord(address)", "0x06ab5923": "setSubnodeOwner(bytes32,bytes32,address)", "0x9d7d6667": "multipliers()", "0x8af49ab7": "maintain(uint256,uint256)", "0x1f3a3a53": "mint(int256,uint256)", "0x74389991": "breakit()", "0x64371977": "set(uint256,string)", "0x3fa6497f": "AdminAddFunds()", "0xba7dc45f": "_removeOperation(bytes32)", "0xf81d087d": "prepareLottery()", "0xd239ea8b": "getSchemasLenght()", "0xa2f3ede2": "computeNameHash(bytes)", "0xa28ecf0b": "sendCryptedHand(bytes)", "0x003b9d88": "setLowerFeePercentage(uint8)", "0x98ea5fca": "depositEther()", "0xb9a0a708": "testChargesAmountApproved()", "0x55291dbd": "claimEther()", "0x2d2800f1": "react()", "0xa9d2293d": "lastClaimBlock()", "0xc45aa04c": "queryShareholders(bytes,uint256)", "0x67af1c81": "getRoundIndex()", "0x50b7b7a2": "setRating(bytes32,uint256)", "0x0aa7881a": "MintableToken(int256,uint256)", "0x0eb3f5a0": "sweepCommission(uint256)", "0x97d47a60": "registerAccountant(bytes,address)", "0xe2c61114": "setImportFee(address,uint256)", "0x6dbe31eb": "testSubBalance()", "0xf5c98aff": "GreeterB(bytes)", "0x79216f5f": "add_monster(uint16,uint16,uint16)", "0x023c23db": "getSize(uint256)", "0x0e1ca8a5": "Oraclize()", "0xa05e822a": "howManyOwners()", "0x313b7b19": "finance()", "0x51a5f2f2": "ConsultingHalf(address,address)", "0x1fb6e99d": "paymentNeeded(uint64)", "0x2bffc7ed": "add(string,address)", "0x5c52c2f5": "resetSpentToday()", "0x39cdde32": "ecverify(bytes32,bytes,address)", "0x64e24f4b": "UpdateClientTokenAccount(address)", "0x0d2560ee": "addMe()", "0xd8589be3": "CoinFlipper()", "0x3b46a7df": "ivote(bool)", "0xa6823189": "parseAddr(string)", "0xd0c24e93": "setNotUpdatable(bytes20)", "0x1f13de92": "inEther(uint256)", "0xb6ce5581": "oraclize_query(string,string[5],uint256)", "0x31ae0019": "KissBTC()", "0xdabf7dc8": "PayoutDividendEarly(uint256,bool)", "0xe3da41b5": "sortWinningNumbers(uint8[5])", "0x8ea98117": "setCoordinator(address)", "0xeff6be2f": "changeBaseFee(uint256)", "0x483a83df": "setKYC(address)", "0xf98a4eca": "executeVote(uint256)", "0x776d1a01": "unvest(uint256,uint256,uint256,uint256,uint256,bool)", "0x1cf52f2b": "isActiveRegistrant(address)", "0x24c9bf5e": "Prizes()", "0xb3822da8": "getContents(uint256[])", "0x999a9965": "setMany(uint256,int256,uint256,bytes,address,bytes)", "0xbade6033": "propose(bytes,uint256)", "0xd38159b8": "testPass()", "0xdabf7ec4": "helper(uint256)", "0xf4993bbd": "executeEmergencyWithdrawal()", "0x46ddb7db": "setAccountBalance(address,uint256)", "0xc12af1ce": "fipsRegister(uint256,bytes)", "0x8f420866": "DEFAULT_SEND_GAS()", "0x62770252": "needsFuneral(uint256)", "0x32921690": "checkDepth(address,uint256)", "0xb3c1a588": "parseMsgData(bytes)", "0x29cd5777": "_tryEraseSingleNode(bytes32)", "0xc6e0c908": "checkTransferFromWithReference(address,address,uint256,string)", "0x9bd99195": "multiAccessChangeOwner(address,address)", "0xa360b26f": "Migrations()", "0x6c6f1d93": "getContractCreationValue()", "0x5445e38c": "_isCycleValid(uint256)", "0x2c85f8e0": "oraclize_query(string,string,string,uint256)", "0xcdda62ad": "FutureBlockCall(address,uint256,uint8,address,bytes4,bytes,uint256,uint256,uint16,uint256,uint256)", "0xc8fdc891": "numberOfMonarchs()", "0xf578fd85": "assertEq0(bytes,bytes)", "0xda311588": "getCoin(uint256)", "0x9a35f886": "__dig_then_proxy(uint256)", "0xfa14df6b": "getChangeRecipientFee()", "0x95f0684b": "getPackageNameHash(uint256)", "0x42909a9e": "create_game()", "0x51582ef3": "sendProxyTransaction(address,uint256,uint256,bytes)", "0xc4e41b22": "getTotalSupply()", "0x110df916": "getChannelID(uint256)", "0x4dc3141b": "CalcAll()", "0xb88a802f": "claimReward()", "0x82b2e257": "getTokenBalance()", "0x9bb0e4df": "getUint(int256,bytes32,string)", "0xc1ae4044": "checkBetColor(uint8)", "0x4a8b5389": "allocateBountyAndEcosystemTokens()", "0xdf06f906": "numBets()", "0xdeb80111": "transfer_asset(address,uint256)", "0x5216aeec": "totalInvested()", "0xe2deaa81": "set_reference(uint256,uint256,uint256)", "0x2ffda1e0": "setBlackflag(uint256,bool)", "0xba45b0b8": "transfer(address,address)", "0x7d7c2a1c": "rebalance()", "0xf32efd3c": "recoverUser(address,address,uint256,uint8,bytes32,bytes32)", "0x4571d4c4": "FutureCall(address,uint256,uint16,address,bytes,bytes,uint256,uint256,uint256)", "0x5fc5d48b": "burnUnsoldCoins(address)", "0x4da47ba0": "TokenSale(address,uint256)", "0x3d9ce89b": "scheduleCall(bytes4,bytes,uint256)", "0x6662e4be": "isWinningBet(uint256)", "0xa501e88d": "Content()", "0x4b70cec4": "getTime(address)", "0x12253a6c": "stopContract()", "0x173825d9": "removeOwner(address)", "0x26121ff0": "f()", "0x7b647652": "LittleEthereumDoubler()", "0x0c5c2ca3": "getIndexName(bytes)", "0x90f2c86d": "convertToWei(uint256,string)", "0x83f95f13": "openClaim(string)", "0xe8b5e51f": "invest()", "0xfdc4b338": "authorizeExtension(uint256,bool,string)", "0x4e116eb8": "unRegisterCertificationDb(address)", "0x5c8a1053": "extend(string)", "0xa932ed0d": "whitelistRemove(address)", "0xa1188e56": "getCurrentDifficulty()", "0xbf1fe420": "setGasPrice(uint256)", "0xead710c4": "greet(string)", "0x144fa6d7": "setToken(address)", "0x42402c2c": "fipsTransferMulti(bytes20[],address)", "0x93e84cd9": "play()", "0x741b3c39": "depositBond()", "0x23306ed6": "getMinimumBond()", "0x5f2e686d": "Ethereum_eight_bagger()", "0x9890220b": "drain()", "0x233120aa": "getChainyURL()", "0xe9dc0614": "vote(bytes)", "0x4df53a0f": "testSetApprovalDb()", "0x5fe27ab0": "createHKG(address)", "0xb56b2627": "add_owner(uint256,address)", "0x6f9a023c": "theultimatepyramid()", "0xc8796572": "collectFees()", "0xea3ebae6": "getConfigBool(bytes32)", "0x213ac932": "addUser(address,uint256,uint8,bytes32,bytes32)", "0xfae9d06d": "calculateTxFee(uint256,address)", "0x45d27edf": "forward_method(bytes,address,uint256,bytes)", "0x8e9ccd04": "computeIndexId(address,bytes)", "0x2f6ae467": "transferDocument(bytes,address)", "0x6e353a1d": "emergencyWithdrawal(address)", "0x7dd56411": "ownerOf(bytes32)", "0x9dc35799": "updateReading(uint256)", "0x246c02e6": "check_depth(uint16)", "0x03d22885": "scheduleCall(address,uint256,bytes4,uint256,uint256,uint8,uint256)", "0x881be8f7": "undo()", "0x953307d8": "revealScissors(string)", "0x7af30442": "testToggleBitFailIndexOOB()", "0x1e0c7ed4": "setConfigBool(bytes32,bool)", "0x2e3be78d": "setPrecisionDirect(uint8)", "0x95a078e8": "hasAccess(address)", "0x245a03ec": "scheduleSetIt(uint256,uint256)", "0xb863bd37": "random(uint256)", "0xa5e62f02": "fallbackRP()", "0x618fa9ce": "getBotBillingIndex(uint256,uint256)", "0x06909f69": "cancel(string,uint256)", "0x2b198366": "addCertifier(address)", "0x57e871e7": "blockNumber()", "0x2b98222e": "getInstitutionByAddress(address)", "0x89eedf00": "setPdfHash(bytes,bytes)", "0x7ac4b05e": "returnMyMoney(uint256)", "0x7fc90182": "Pool(uint256)", "0x291e6777": "sendVote(uint256,uint256)", "0x579badf6": "UniversalFunction(uint8,bytes32,bytes32,bytes32,bytes32,bytes32)", "0xe1f21c67": "approve(address,address,uint256)", "0xa1b9af31": "unlockBets()", "0x92d0d153": "t()", "0x095ea7b3": "approve(address,uint256)", "0x9cb31079": "setLowLimit(uint256)", "0xb971b4e5": "setNotTransferable(bytes20)", "0x2a745971": "BlockKing()", "0x582ca57b": "get_associations()", "0xa0d605c6": "addCertificationDocumentInternal(address,bytes32)", "0x85233869": "NumberOfMiners()", "0xb0349184": "clearRecords(bytes32[])", "0x3e8f5b90": "setConfig(string,uint256)", "0x7c73f846": "getMinimumEndowment(uint256,uint256,uint256)", "0xcc9ae3f6": "getMyReward()", "0xac900c2d": "unregisterSeller(address)", "0x306df22d": "GPSDestination(int256,int256,uint256)", "0x5e968a49": "ownerSetMaxProfitAsPercentOfHouse(uint256)", "0x80dcaf27": "getRefNumber()", "0x4245b0f7": "Lottery()", "0xe46164c5": "waitingForPayout()", "0xa8c3ec48": "oraclize_query(uint256,string,string[2])", "0x58ea80e5": "setThroneCreationPrice(uint256)", "0x8a4fb16a": "getWithdrawal(uint256)", "0x4464aec7": "testTryGet()", "0xf3c37bd5": "Verifier(address,uint256,uint8)", "0xb4c4e005": "testTransferToAcceptAuthority()", "0x346b306a": "oraclize_query(string,string,string)", "0x24032866": "checkExecutionAuthorization(address,uint256)", "0x509f8633": "create_account()", "0x26da8e17": "ownerUpdateCostToCallOraclize(uint256)", "0x74a93e6c": "setTokenHolder(address,address)", "0x1baaeb91": "getSignature(bytes4,uint256)", "0x337c1e28": "getIndexRoot(bytes)", "0xac92fdb5": "getSaleDate(bytes16,uint256)", "0x13b2663b": "cash_received(string)", "0x68e4bd99": "testSetBitSuccess()", "0x20620f37": "onAuctionEnd(string)", "0x85528394": "currentClaimPriceWei()", "0x1995333b": "burnFunds(uint256)", "0x5184ffc9": "setAmbiAddress(address,bytes)", "0x269975d0": "GameDetails(uint256)", "0x29274fe1": "buyBOTx(uint256,string,string,address,uint256)", "0x720c4798": "workshop()", "0x6f9a5eab": "createTx(uint256,address,uint256)", "0xb66a323c": "claimThrone(string)", "0x14cabddb": "joinProof(uint256)", "0x76e4ca0d": "voteQuorum(uint256,bool)", "0xbcb3b5d2": "getGamblesList(uint256)", "0xf2080ba6": "Pong(int8)", "0xab470f05": "getCaller()", "0xe0b1cccb": "updateBalance(address,uint256)", "0x4889ca88": "receiveApproval(address,uint256,address)", "0xbc4b3365": "addFunds(address,uint256)", "0x5d1a3b82": "getOutcome(bytes32)", "0x8204ecdd": "getFee(bytes)", "0x49aa4ee2": "removeVote()", "0x9131d803": "testSetFrontend()", "0x72929b56": "getKudosPerProject(address)", "0x287418e7": "query(uint256,uint16)", "0xbb7859b5": "periodThree()", "0x7dc5cd32": "_patternToNumber(bytes)", "0xf7ea7a3d": "setTotalSupply(uint256)", "0xb3f98adc": "vote(uint8)", "0x655388be": "walkTowardsBlock()", "0x856deacf": "findTag(string)", "0xcaab0acc": "testThrowCreateRetracted()", "0xede8ebf3": "checkApprove(address,uint256)", "0x4296a9cb": "getNodeRightChild(bytes)", "0x03cf4fd6": "expire(uint256,uint256,uint8,bytes32,bytes32,bytes32)", "0x87045369": "setCanCall(address,address,bytes4,bool)", "0x0ed21029": "getIssueAssignee(uint256,bytes32)", "0xc5958bda": "removeFile(bytes)", "0xa8026912": "setSource(address)", "0x05a5b8c6": "verifyTx(bytes,int256,int256[],int256,bytes,int256,int256[],int256)", "0xed3058e0": "transferRight(address,bytes)", "0xba487e62": "newCampaign(uint32,uint96,uint16,uint16)", "0xd6c19fe0": "build(bytes,uint256,uint256,address)", "0xebb741cb": "getChannelSize(uint256)", "0x3b996f40": "quarter(uint32,uint32,uint32,uint32)", "0x0b7373d6": "giveAllBack()", "0x3f887fad": "buyShares(uint256,uint8,uint256,uint256)", "0xb5deeca7": "BaseRegistry()", "0x6dd6e87b": "checkOut(int256)", "0x90a85119": "checkBetResult(uint8)", "0x724121ae": "contentExists(uint256)", "0x67bd69a6": "getLastDuel2()", "0x821e9169": "testFailControllerChargeMoreThanApproved()", "0x083ae1fe": "setPackage(string)", "0x8eaa6ac0": "get(bytes32)", "0xf42aa287": "getBlobStore(bytes12)", "0x32a2c5d0": "getContractAddress()", "0x69d89575": "releaseFunds()", "0x984ac378": "lotteryTitle()", "0x3baf4e1e": "newPayment(uint256,uint256)", "0x938c4307": "scheduleCall(bytes4,bytes,uint16,uint8,uint256,uint256,uint256,uint256,uint256)", "0x47372325": "getChannelSize(address)", "0xcb10e0c5": "getLastDuel1()", "0x1a0919dc": "unregister(bytes32)", "0x6a3c1198": "_projectCancelNew()", "0x976b01c0": "setNotRetractable(bytes20)", "0x44d03ac6": "BlockhashFetch(address)", "0xb0fd935b": "registerCertificationDb(address)", "0x2c7c4549": "PurchasableToken(uint256)", "0x3e0a51b4": "TweetAccount()", "0xc5f310c0": "register(bytes12)", "0xc10dd4c6": "getEvents(bytes32[],address)", "0x37a6b9f8": "recordCallback(address,uint256,bytes,bytes)", "0x1b9265b8": "pay()", "0xc91d7e9c": "getFee(bytes32[])", "0xf6c5c80d": "cleanUp()", "0x590e1ae3": "refund()", "0x9c709343": "split(bool,address)", "0xe68d3ae3": "escrow(uint256,string,address,uint256)", "0xa1cb31b7": "_state()", "0xfbac3951": "isBlocked(address)", "0x29dfdded": "addNewDonkey(address)", "0x7bc49a95": "play(uint256,uint256)", "0x5329c681": "checkTimeout(uint256)", "0x7c05caf3": "testCreateCostAuth()", "0x8606f905": "balanceOf(address,bytes)", "0xbe45fd62": "transfer(address,uint256,bytes)", "0xd7130651": "getCity(uint256)", "0xb2353d69": "updateRightLottery(address)", "0x01bb85a4": "__startBlock(string)", "0x4316abbb": "newJester(address)", "0x5c3e426c": "adminRetrieveDonations(address)", "0x293ffca3": "AddressReg()", "0x4156fdb7": "createSwap(uint256)", "0x2852b71c": "accept()", "0x027a5e3f": "getLastVersion(bytes)", "0x3d90d44d": "addPowerSource(address,uint256,uint256)", "0x1ab06ee5": "set(uint256,uint256)", "0x50f07cf9": "setReadingDelay(uint256)", "0x669e48aa": "get(uint256,uint256)", "0xdea9c72b": "getLatestPreReleaseTree(bytes32,uint32,uint32,uint32)", "0x60f66701": "useCoupon(string)", "0x2431f164": "process_payment()", "0x17623e5b": "unauthorizeManager(address)", "0x367bbd78": "strlen(string)", "0xbab86ea8": "test(string,string)", "0x25f3da52": "GetBankAccountNumber()", "0xebb045fa": "PublicResolver(address)", "0xdd57d5c5": "setTrust(address)", "0x22e803c2": "transferBounty()", "0x12c8052f": "won()", "0x3535cd52": "setDailyCosts(uint256)", "0x92d8c8cf": "setupImportFee(address,uint256)", "0x53caf582": "testThrowSetNotUpdatableNotOwner()", "0xb3ade772": "shipProducts(string,string)", "0x61472fd4": "CSGOBets()", "0x5c7c9aa4": "checkAccountState(address)", "0x6560a307": "suggestedGas()", "0xd0d552dd": "setAsset(address)", "0xc02f081a": "shiftBits(bytes,int256)", "0x9348cef7": "reveal(uint256,uint256)", "0x5ca1c5a0": "getNodeValue(bytes)", "0x34c1b4ba": "sha(bytes)", "0xe0117441": "setRegistrationPrice(uint256)", "0x92698814": "reserved(bytes32)", "0xaa1e84de": "hash(bytes)", "0x2125b65b": "transfer(uint32,address,uint224)", "0x9f9eac67": "ChangeName(string)", "0x7eb69ba1": "hint(int256,bytes32,string,bytes20)", "0xc6888fa1": "multiply(uint256)", "0xa9059cbb": "transfer(address,uint256)", "0x84c344fe": "_register(bytes4,string)", "0x744d8b4f": "recordWin(uint256,uint256)", "0xad1ef61e": "donkeyInvested(address)", "0xb4787dc5": "linkEID(bytes,bytes)", "0x7b1cbb13": "getChannelValue(bytes)", "0xd0febe4c": "buyTokens()", "0x74f519db": "setLastTimestamp(uint256,uint256)", "0xe30081a0": "setAddress(address)", "0x6fd902e1": "getCurrentBlockNumber()", "0x25d8dcf2": "betAndFlip()", "0xc062f578": "updateStage()", "0xe0cfc05c": "testThrowsRetractLatestRevisionDoesntHaveAdditionalRevisions()", "0x854f4817": "buyKissBTCWithCallback(address,uint256)", "0x58b1f29c": "refundBounty(uint256)", "0x0645b5d5": "getMyShareholderID()", "0xe9c63b9c": "requestPeerBalance()", "0x1b03316f": "getSecond()", "0xbcc6092a": "MyEtherBank()", "0x1cf43b63": "extractExportFeeChargeLength()", "0x5e58f141": "shares(address,bytes,int256)", "0x6103d70b": "withdrawPayments()", "0x3e0663e0": "AdminDrawProcess()", "0xa0f029fc": "ContractorInterface(address,address,address)", "0xe604cf9f": "get_all_squares()", "0xd13d1ace": "scheduleCall(bytes,bytes,uint16,uint8,uint256,uint256,uint256,uint256,uint256)", "0xb74bc710": "LuckyDoubler()", "0x611f69de": "__proxy_motion(address,uint256,uint256,bytes)", "0xbb814e9e": "versionExists(bytes32)", "0x545e7c61": "deploy(address,address)", "0x1b437d0c": "compareLastCalldata(bytes)", "0x845051d3": "testContractsNotNull()", "0x23de6651": "emitTransfer(address,address,uint256)", "0xd78c20ff": "voteApprove(uint256)", "0x6f13e01b": "EthVenturePlugin()", "0x1f6b0a9d": "getReleaseLockfileURI(string,uint32,uint32,uint32,string,string)", "0x13d4bc24": "buyTokenProxy(address)", "0xd509b16c": "testWithdraw()", "0x2f54bf6e": "isOwner(address)", "0xf60381a1": "stra2cbor(string[])", "0x34dbe44d": "getLastBlockNumberUsed()", "0x28dcfdac": "getSignsCount(uint256)", "0x2888f9d0": "updateMaxBet()", "0xc3b2556d": "lookup(bytes)", "0x6fbaaa1e": "currentMultiplier()", "0xe241c1d9": "deriveKey(uint256,uint256,uint256)", "0x5b37e150": "create(bytes32,bytes)", "0xa1c95ac2": "GSIToken(uint256,string,uint8,string,address)", "0xfc9e53df": "setNextRegistrar(address)", "0x34e8980f": "bootUpHangouts()", "0x457dd8b3": "setMasterKey(address)", "0xdb833e3a": "sellShares(bytes32,uint8,uint256,uint256)", "0x1e5330ca": "checkBetResult(uint8,address,bytes32,bytes32)", "0x4a00a522": "homebase(int256,int256)", "0xb938bf42": "sendBounty(bytes32)", "0x46be96c3": "amountFilled(address,uint256,address,uint256,uint256,uint256,address,uint8,bytes32,bytes32)", "0x2fa00e58": "fipsTransfer(bytes20,address)", "0x95669952": "debtor(address,uint256)", "0x3f19d043": "getContributions(address)", "0xeb455dc6": "sendBitcoin(string,uint256)", "0x034cb28e": "addressOf(address,bytes)", "0x1e26fd33": "setBool(bool)", "0x1b55ba3a": "Start()", "0x6f3fe404": "updateBalances()", "0xc45b415e": "createRequest(address[4],address,uint256[11],uint256,bytes)", "0x180aadb7": "underLimit(uint256)", "0xb44bd51d": "getConfig(string)", "0x93d79105": "hashRelease(bytes32,bytes32)", "0x9f8a13d7": "isActive(address)", "0x257bcd6a": "placeBet(uint256,bytes32,bytes32)", "0xdd10d97e": "getPlayerWaiting()", "0x6d16f79c": "__transferWithReference(address,uint256,string)", "0x6ce1417e": "Fund()", "0x67beaccb": "scheduleCall(bytes)", "0x4b0697e4": "Manager(address)", "0x6a7fc8b7": "setDailyWithdrawLimit(uint128)", "0x7a6e9df7": "getTimestamp(bytes)", "0x797af627": "confirm(bytes32)", "0x81183633": "setStandard(bytes32)", "0x6a1db1bf": "changeFee(uint256)", "0x4e6ab570": "insert_order(address,bool,uint32,uint128)", "0xa4e2d634": "isLocked()", "0x7c79ebce": "expired(uint64)", "0x20965255": "getValue()", "0xfd408767": "fireEventLog4()", "0xcf6b3822": "WatchCollectedFeesInSzabo()", "0x8f7fe231": "ValidetherOracle()", "0xbf12165e": "fillUpSlot(uint256,uint256)", "0xdabdc1f2": "ChangeActiveDigger(address)", "0xe9e7a667": "get_stake(bytes32)", "0x0ad95b44": "bribery()", "0xdb6fcf01": "is_destroyed(uint256)", "0x4378a6e3": "getAttributes(uint256)", "0x71589d6b": "newponzi()", "0x47274dbe": "disableUser(address,address)", "0xb40a5627": "bidCount()", "0xf1eae25c": "mortal()", "0x13af4035": "setOwner(address)", "0xaf030d2c": "setResult(uint256,uint256,bytes32)", "0x098ab6a1": "snapshotCount()", "0x27cca148": "lastClaimedBlock()", "0x940c154b": "lockBet(uint256)", "0x378c0605": "buyTickets(address)", "0xbcfcb03e": "allocateFounderTokens()", "0x0138e31b": "_jAdd(uint256,uint256,uint256,uint256)", "0x0a7f4239": "getAccountFundContract(address)", "0xc96593a0": "The10ETHPyramid()", "0x22beb9b9": "scheduleDoIt(uint256)", "0xdb0e127a": "openDoor()", "0x3dc02266": "fipsRegister(uint256)", "0x7d242ae5": "setBasePrice(uint256,bytes)", "0xe82b7cb2": "proxySetCosignerAddress(address,bytes32)", "0xa60bbcd3": "ModelCoordinator()", "0xc26aa3c9": "lockUnicorn(uint256)", "0x96ff7e97": "requestIdentity()", "0x99753de7": "clear_level()", "0x69d79ad5": "moneySumAtSettlement(address,uint256,uint256,int256,uint256,uint256)", "0xda359dc8": "setBytes(bytes)", "0x6edb4cf6": "testThrowRetractLatestRevisionDoesntHaveAdditionalRevisions()", "0x9d170c5d": "getRef(string)", "0x11cd98ed": "convertToAllTable(uint256,string)", "0x67f809e9": "DynamicPyramid()", "0xd5f37f95": "sign(uint256,uint256,address)", "0xf5562753": "getClaimAmountForBlock(uint256)", "0xc9bbc8c0": "donkeyName(address)", "0x5858ef10": "testErrorNonOwnerCantBreach()", "0x74388347": "checkBetDozen(uint8,address,bytes32,bytes32)", "0xee564544": "_slotCancelNew()", "0xf3bb9741": "commitmentCampaign(uint256,bytes32)", "0x2b68b9c6": "destruct()", "0xa9b8f7b8": "ProtectTheCastle()", "0x16181bb7": "shortSellShares(bytes32,uint8,uint256,uint256)", "0xb524abcf": "totalSupply(bytes32)", "0x8006745b": "getPayout(address)", "0x137c638b": "getExtraGas()", "0x824d5603": "getIndex(uint16,uint16)", "0x245a6f74": "isProxyLegit(address)", "0x9eded57a": "paybackLast()", "0x7b1aa45f": "ownerDeposit()", "0x974654f4": "requiredGas()", "0x76d690bb": "BountyList()", "0xf4b2dfea": "Matching_Finneys()", "0xbd66528a": "claim(bytes32)", "0x85eac05f": "changeOwnerAddress(address)", "0xa69df4b5": "unlock()", "0xe6d9bb0f": "secondsUntilEnd()", "0xcd57a448": "SwapContract(address,uint256)", "0xb245fc92": "findNextMonth(uint256,bytes)", "0x7620f4bb": "fipsNotaryLegacy68b4()", "0x61886014": "combineDice(uint8,uint8)", "0xdf4ec249": "step3()", "0x2262cd94": "wroom()", "0x1099d3ec": "scheduleTransaction(uint256,uint256,uint256,bytes)", "0xd8c34127": "isKnownSignature(string)", "0x8afa08bd": "setDrawDate(uint256)", "0xdb18c972": "play4(address,uint256)", "0x2f30283e": "testSomething()", "0x8ca17995": "divest(uint256)", "0x1ef3755d": "restart()", "0x99bb875c": "funeralAndBirth(bytes,int256,bytes)", "0x157ad5a1": "canWithdrawBond(address,uint256)", "0xfd8055d2": "updateBOTBillingInfo(uint256,string,address,string,string,uint256)", "0xa10889fa": "setVersion(uint32,uint32,uint32,string,string)", "0x51cff8d9": "withdraw(address)", "0xbe999705": "addFunds(uint256)", "0x2e5d1042": "requestPayout(uint256,uint256,bytes32,uint256,uint256)", "0xb50954b6": "cancelWaitingForOpponent()", "0xc42cd8cf": "etherSplit(address,address)", "0x42ce1488": "upload(string)", "0xad04592e": "owner_deposit()", "0xc2cf7326": "hasConfirmed(bytes32,address)", "0x1dbf3bc7": "spend(uint256)", "0x36b81feb": "Deed(address)", "0xf47289e1": "_ecDouble(uint256,uint256,uint256)", "0x026993e0": "Midas(address,address)", "0x5e404de3": "setMaximumCredit(uint256)", "0x0194db8e": "sum(uint256[])", "0xa04a0908": "execute(address,bytes,uint256)", "0x2b4a3b31": "doTransferFrom(address,address,uint256)", "0x96ed10a4": "issuePOIs()", "0xb75c7dc6": "revoke(bytes32)", "0x6056969b": "announce(bytes32)", "0xd63547e6": "GetFreeCnt()", "0x788e26e7": "sponsorDeposit()", "0x550dd006": "calcCostsBuying(uint256,uint8,uint8,uint256)", "0xd4b1d19f": "testThrowsTransferDisabled()", "0x04706fdf": "giveContributionsBackProfitBugged()", "0x5d5483b3": "WatchAppliedFeePercentage()", "0x6bf8f85a": "forceFinish()", "0x3edd90e7": "NewOwner(address)", "0x7c69b5d1": "NewDeposit(uint256)", "0x866f6736": "trustedChildWithdraw()", "0xcdcb7c8f": "chase()", "0x60dccd89": "getContentAccount(uint256)", "0xbff1f9e1": "totalUsers()", "0x1aca00fd": "variable(uint256)", "0x6e658fbe": "myFundsExpireIn(uint256)", "0xddb1bdc8": "credit(address,uint256,uint256)", "0x934bc29d": "exampleFunction(uint256)", "0x113e6b66": "fipsAddToLedger(bytes20,address)", "0x1a88bc66": "slot()", "0xec97cff7": "addCertificationDocument(address,bytes32)", "0x0790e880": "setBlockappsAddr(address)", "0xe0ad411d": "assets(bytes)", "0x791b51f1": "Consulting(address,address)", "0xa26dbf26": "totalParticipants()", "0xb78b52df": "allocate(address,uint256)", "0xdb29fe12": "addShareholder(address)", "0x06459119": "testThrowsTransferNotTransferable()", "0xbadbaa3c": "setCallData()", "0x2c6b2c92": "checkProfitLossSinceInvestorChange()", "0x8aa6f1b1": "setUltimateOutcome(bytes32)", "0xecb98714": "random_damage(uint256)", "0x506e106c": "setToS(string)", "0xf0d474f9": "underdogCount()", "0x2212dbc3": "get_timestamp()", "0xd504ea1d": "getArray()", "0x9b29cb23": "getDailyPayment()", "0x9d3e069c": "StartDraw()", "0x12494160": "isHolder()", "0xbbd4e8c9": "numDeposits()", "0xfea2920e": "createNewDraw()", "0xff556ecb": "releaseUnicorn(uint256)", "0x3bed33ce": "withdrawEther(uint256)", "0xaa9669c1": "roll(uint256,bytes)", "0xa00aede9": "scheduleCall(uint256,address)", "0xc0819961": "Invest()", "0x1ed24195": "getPeriod()", "0x5babb758": "testSetUp()", "0xaaf9d13e": "buyTopDog(uint256,uint256)", "0x7c45ef6c": "stringToSig(string,string)", "0x7353f62b": "testGetApprovalDb()", "0xef7507c8": "testWinner(uint256)", "0x7ef95c6f": "extractAccountAllowanceRecordLength(address)", "0x66099706": "getChannelCred(address,uint256)", "0x5c242c59": "query1(uint256,string,string,uint256)", "0x299a7bcc": "setOwner(address,address)", "0xe1152343": "payout(uint256)", "0xd40a71fb": "step1()", "0xda9c6a46": "getReplyCount(uint256)", "0xffb7bfba": "watchProposal(uint256)", "0x2e1a7d4d": "withdraw(uint256)", "0x03da8902": "transfearDBOwner(address)", "0xc9bd2893": "fines()", "0xfdd3a879": "quick()", "0xda0774ad": "getCallFeeScalar(uint256,uint256)", "0x0f2c9329": "split(address,address)", "0xa3912ec8": "receiveEther()", "0xfd6f5430": "setContent(string,bytes32)", "0x99e0021f": "mergencyCall()", "0xb7aec6a5": "scheduleCall(address,bytes,uint256,uint256,uint8,uint256)", "0x23145ca0": "forceCheck()", "0xc7cf28fe": "canClaimTimeout()", "0x26db7648": "proposedVersion()", "0x60a60fd8": "testProxyCallWithValue()", "0x044d0b06": "oraclize_query(string,string[2])", "0x4f013184": "investInTheSystem()", "0x0c9fd581": "assertTrue(bool)", "0x09574810": "getOperationsNumber()", "0x6e2edf30": "ETCSurvey(address)", "0x3cc86b80": "GetMoney(uint256,address)", "0xf7b89a3e": "getTotalCosts()", "0xb18c6847": "manualUpdateBalances()", "0x8a65d874": "userStats(address)", "0xf80b3cfa": "checkBetLowhigh(uint8)", "0xc2def3b9": "getOrganizer()", "0x2dae9878": "BankOwner_EnableConnectBankAccountToNewOwnerAddress()", "0x1998aeef": "bid()", "0xc64e8bc0": "executeN(uint256)", "0xd4088e33": "setPrice(uint256,uint256,uint64)", "0xd263b7eb": "ownerkill()", "0xc478fc37": "EtherWheel(uint256,uint256,uint8)", "0x05b765ea": "getCertifierStatus(address)", "0x93c32e06": "changeFounder(address)", "0xf207564e": "register(uint256)", "0xae6c0b03": "canWithdrawBond(uint256)", "0x2b1071c9": "testTransferToNullAuthority()", "0xd9feeeb6": "fillMyOrder(uint256)", "0x9fb755d7": "setHotWallet(address)", "0x7f98444f": "randomEnd()", "0xf3fef3a3": "withdraw(address,uint256)", "0x48a0d754": "available()", "0x3af94817": "getPongvalRemote()", "0xec21a913": "setUint256(int256,uint256)", "0x6099af40": "setConfigBool(bytes,bool)", "0xf0cbe059": "proxyTransferFromWithReference(address,address,uint256,bytes32,string)", "0xf93589ce": "didWin(bytes)", "0x1eb5ea2e": "returnFunds()", "0xa6027d53": "IconomiTokenTest(uint256,string,uint8,string,uint256)", "0x8a323b38": "Contract(uint256,string,uint8,string)", "0xb0f07e44": "registerData()", "0xc9d27afe": "vote(uint256,bool)", "0x64265b1a": "share_transfered(string)", "0x78205f67": "testThrowTransferEnableNotTransferable()", "0x081780f4": "clearRecord(bytes32)", "0xdd137b5d": "toBase58(uint256,uint8)", "0x9483e91a": "withdraw(address,uint256,bytes,uint256)", "0xc6502da8": "basePayment()", "0xe17e1274": "testTransferToRejectAuthority()", "0x9af605cb": "__proxy(address,bytes,uint256)", "0x7a8df1b9": "getAffiliateInfo(address)", "0x46b305d6": "lockBetsForWithdraw()", "0x7d4cf602": "buildDSBalanceDB()", "0xce87f626": "replaceWizardRP(address)", "0x125b8f06": "isInNextGeneration()", "0xd0068f80": "getClient(uint256)", "0x7f0899f2": "AddTicket(bytes5[])", "0xb15dcc25": "query(address,bytes2,uint256)", "0x07a9574a": "changeLeaderMessage(string)", "0x16e55626": "getDogName(address)", "0xbc058968": "updateThingData(bytes32[],bytes32[],uint88)", "0x02aa274b": "setForward(bytes4,address)", "0x08f235ec": "getDefaultPayment()", "0x1dd4914b": "withdrawEtherOrThrow(uint256)", "0x7ca31724": "tokenId(address)", "0x0c4f65bd": "getOwnerAddress()", "0xeec3cb41": "placeBet(bool[],uint256,uint256)", "0x9054bdec": "toTimestamp(uint16,uint8,uint8,uint8,uint8,uint8)", "0x468f02d2": "getUnderlyingPrice()", "0x74331be7": "sete(address)", "0xb05e390a": "TokenEther(string,string)", "0x89cc5ea8": "bid(string,address,uint256)", "0xa8893a6e": "getNumOfSalesWithSameId(bytes16)", "0x3defb962": "heartbeat()", "0x15a03930": "TossMyCoin()", "0x1d8ae626": "Security(string,string)", "0xf1bca7a4": "doCall(uint256)", "0xae6215d8": "getBlockHeight(bytes)", "0x8124bb0f": "continueExecution()", "0xc1cbbca7": "contribute(uint256)", "0xa48566ba": "serverSeed(address,bytes)", "0xc0f5a9cb": "deleteThing(bytes32[])", "0x4136aa35": "isAlive()", "0x6fe665e9": "SlotMachine()", "0xfaff50a8": "rootNode()", "0xaf769eff": "Paper()", "0x77863b61": "CrossWhitehatWithdraw(uint256,address)", "0x2bed55b0": "buildDSEasyMultisig(uint256,uint256,uint256)", "0xd2ef7398": "challenge()", "0x96e4ee3d": "convert(uint256,uint256)", "0x2dff6941": "content(bytes32)", "0x4d536f9f": "validateNameExt(bytes)", "0xd4d5d32a": "collectFee()", "0x6620a935": "sendToOwner()", "0x5084da18": "fipsOwner(bytes20)", "0xe419f189": "multiAccessIsOwner(address)", "0xa9fbc614": "lookupTicketHolder(uint256)", "0x11f72496": "testT()", "0x7365870b": "bet(uint256)", "0x09861b81": "flooredSub(uint256,uint256)", "0xe28fed1e": "userRescues(address)", "0x28cc413a": "getProof(uint256,uint256,uint256)", "0x9a8f09bd": "newKing(address)", "0x5d068051": "sendFees(address)", "0x49cbe338": "tryRead(uint64)", "0x691bfc89": "goods(uint16,uint256)", "0xfc36e15b": "vote(string)", "0x48107843": "getNextCallSibling(address)", "0x6461fe39": "transferFromWithReference(address,address,uint256,string)", "0x804e11dc": "testThrowsDisownNotTransferable()", "0x76f30ca1": "toContentID(address,uint256,string,bytes)", "0xa77b2e37": "Coin()", "0x3f2f1596": "setupTreasury(address,uint256)", "0x3de9e4c6": "__transferFromWithReference(address,address,uint256,string)", "0x612e45a3": "newProposal(address,uint256,string,bytes,uint256,bool)", "0x4cdb48e4": "isValidNym(address)", "0x5afa5036": "isCertified(address)", "0x9a1b420b": "OraclizeAddrResolver()", "0xec5c9036": "Crowdsale(address,uint256,uint256)", "0x01095962": "oraclize_setCustomGasPrice(uint256)", "0xe7e2aa0e": "buyer_cancel()", "0x2da8f764": "submitVideo(string,string)", "0x3395dc70": "acceptTransfer(address,address,uint256)", "0xdf143fb7": "HackerGold(address)", "0x63334c58": "transferETC(address)", "0x6a5da6e5": "followCampaign(uint256)", "0x49fb2dc5": "add_to_association(uint256,uint256,uint256)", "0x953aa435": "GetPrice(uint8)", "0xc1257bad": "testPassingAProposal()", "0x3bc5de30": "getData()", "0x4abb9d39": "depletable()", "0x129484b6": "changeFeeRecipient(int256,int256,int256,int256,int256,int256)", "0x902e64e5": "Oath()", "0x8c4dd5cd": "Democracy()", "0xbea124a6": "query(bytes,bytes,int256)", "0x69347990": "ownerWithdrawl()", "0x606deecd": "requestData()", "0x6720ceb1": "sendPayment()", "0xb1050da5": "newProposal(address,uint256,string,bytes)", "0xfeaa29d8": "insertProfitHere()", "0x36f9f49c": "etherandomSeed()", "0xae815843": "query(uint256,string,string,uint256)", "0x752d349c": "depthCheck(int256,int256)", "0xa24835d1": "destroy(address,uint256)", "0x26070774": "Token(address)", "0x6e0d98fe": "setProbabilities(uint32[])", "0x0761a004": "step(uint256,bytes)", "0xad82dcac": "testBlockhashCorrectFee()", "0x0900f010": "upgrade(address)", "0xa288fb1f": "setConfigUint(int256,bytes,uint256)", "0xe88b8ac6": "confirmAndCheck(bytes)", "0xf1a00a53": "unregisterListening(address)", "0xb17acdcd": "collectFees(uint256)", "0xb5d0f16e": "getGasScalar(uint256,uint256)", "0xae999ece": "reserve(string)", "0x95ceb4b3": "winningProtocal()", "0x1a93fa4b": "reorganizeSubUsers()", "0x9243e088": "setEnforceRevisions(bytes20)", "0x342454c7": "isDigit(bytes1)", "0x8e2c6f4d": "initiateVerification(address,bytes,bytes)", "0xddb5b3ac": "SellTokens()", "0xd18dfdc9": "parrot(uint256)", "0xd3732642": "FastRealisticPyramid()", "0x37ab8f20": "notifyPlayer(uint256,uint256,uint256,uint256)", "0xfb6e155f": "availableVolume(address,uint256,address,uint256,uint256,uint256,address,uint8,bytes32,bytes32)", "0x50ab6f7f": "getMsgs()", "0x6b6a53fa": "testThrowsRestartNotOwner()", "0x4dc7cc55": "terminateAlt()", "0x8b9726c1": "multiAccessCallD(address,uint256,bytes,address)", "0x5a74dee5": "multiAccessRemoveOwnerD(address,address)", "0xfd339d18": "testAuthorityTryAuthUnauthorized()", "0xd3d6a975": "testThrowsTransferNotEnabled()", "0x8aa001fc": "getSecond(uint256)", "0x2ec2c246": "unregister(address)", "0xbbdb31cb": "challenge(uint256,address,bool)", "0x635cfda2": "Incrementer()", "0x704b6c02": "setAdmin(address)", "0xc1c723f4": "validateProposedMonarchName(bytes)", "0xadf5e565": "verify(bytes,address,uint256,uint8,bytes,bytes)", "0xbe7cddf8": "TwoD()", "0xc71b583b": "closeRequest()", "0x9cbf9e36": "createToken()", "0x69c4113d": "setNewBudget(uint256,uint256,uint256,uint256)", "0xd4649fde": "expire(uint256,uint8,bytes32,bytes32,bytes32)", "0xf9a7a2ef": "named(bytes)", "0xdbf45aa3": "EthBank()", "0xbb3ce7fe": "DepositHolder()", "0xfb5d5729": "getPongvalTransactional()", "0x2f0b15f6": "testGetUnset()", "0x62891b5d": "multiAccessChangeRequirement(uint256)", "0x538e0759": "refill()", "0x24c65f35": "updateRefundGas()", "0x62b24189": "DepositToBankAccountFromDifferentAddress(uint32)", "0xd81f53fd": "EtherId()", "0xcea943ee": "getSaleConfig()", "0x23647398": "testThrowRetractNotOwner()", "0xcdcd77c0": "baz(uint32,bool)", "0xa677fbd9": "example2Func()", "0xa02b9aac": "getPaymentDataByAddress(address)", "0x268eb055": "setDescription(uint64,bytes)", "0x09d2d0b9": "setServiceAccount(address,bool)", "0x5f70d9ac": "getBot(uint256)", "0x63f80de3": "issueCoin(address,uint256,uint256)", "0x8570153e": "publish(string,string,bytes,address[])", "0x7975c56e": "oraclize_query(uint256,string,string)", "0x7c3064f1": "refundStake()", "0xef4ffee2": "Honestgamble()", "0xfdc193a4": "test3Fails()", "0x0eb495c2": "pushCity()", "0x6a357465": "payHours(address,uint256)", "0x93f0bb51": "order(address,uint256,address,uint256,uint256,uint256,uint8,bytes32,bytes32)", "0x0a9254e4": "setUp()", "0xc490a266": "toUInt(bytes)", "0xf666323e": "UUIDProvider()", "0x857d4c07": "throwScraps(uint256)", "0x7fd8ee68": "computeNameHashExt(bytes)", "0x7bd703e8": "getBalanceInEth(address)", "0x68f65f02": "ChangeShownDenomination(bool,bool,bool,bool)", "0xc7f758a8": "getProposal(uint256)", "0x824dbc9a": "changeMembership(address,uint256,bool,string)", "0x2a228fc2": "processWithdrawals()", "0xe0457884": "betResolution(uint8,uint8,uint8,bool)", "0x550538f6": "getOneTimeCosts()", "0xf3e84cf3": "createNewRevision(bytes32,bytes)", "0x77fe38a4": "transferToICAPWithReference(bytes32,uint256,string)", "0xcc189d00": "Vault(address,uint256)", "0x9e281a98": "withdrawToken(address,uint256)", "0x7b12df39": "userProfits()", "0xa843c97f": "attack(uint256,uint256,uint256[])", "0x9da680f3": "adjustRegistrationFee(uint256)", "0x3ea3f6c5": "activateRegistrar()", "0xb36df681": "ExecutableBase()", "0x0f3a1412": "getArrlist(uint256,uint256)", "0x8b7f0ddd": "register(address,address,string,string,bytes32[],uint256,string)", "0xb5d3a379": "CanaryTestnet()", "0x9894221a": "SendCashForHardwareReturn()", "0xc2b12a73": "setBytes32(bytes32)", "0xff1b4341": "easyPropose(address,uint256,uint256)", "0x927ed13a": "newClient(uint256,address)", "0x9b9d0364": "_setFeeStructure(uint256,uint256,uint256)", "0x01518d76": "sendQuery(uint256)", "0x4112b7f1": "tryGetNameOwner(bytes)", "0xb759f954": "approve(uint256)", "0x810a882f": "setConfigBytes(bytes32,bytes32)", "0xea7a7184": "testGetBalanceDb()", "0xb0c8f9dc": "add(string)", "0xe59f611f": "InputLimit(uint256)", "0xdce293a7": "minLength(uint256)", "0xf509b627": "confirm(address,uint224,uint32,address)", "0xd48bfca7": "addToken(address)", "0x044f9ac8": "findThroneCalled(bytes)", "0x1d57bcf7": "ProofLibInterface()", "0x75830463": "checkBetLowhigh(uint8,address,bytes32,bytes32)", "0x2a45a39a": "Post(address)", "0x29cbdc86": "buyin(address,uint256)", "0x3cbfed74": "getBondBalance()", "0x80a23ddf": "mintBadge(int256,address,uint256)", "0x96b76c23": "stand(uint256)", "0xc392f5a0": "getAllPackageReleaseHashes(string)", "0x4847a79c": "_transfer(address,uint256)", "0x905e6e42": "JSON_Test()", "0x9f489e4e": "getDeposit(uint256,address)", "0x4f8e624e": "Greeter(string)", "0x96013c9c": "testLatestPkgGetter()", "0xe4fc6b6d": "distribute()", "0x423d4ef2": "createChannel()", "0x24d7806c": "isAdmin(address)", "0x691fb8ea": "jumpIn()", "0xd50f6bf0": "transferETH(address)", "0xd0e0813a": "promote(address)", "0x528eedcb": "sendSafe(address,address,uint256)", "0x00faf4dd": "getTokenDivisor()", "0x46a1d95f": "closeMarket(bytes)", "0x318a3fee": "relayTx(bytes,int256,int256[],int256,int256)", "0x49d55d9d": "receiveTransfer(uint256)", "0x4fa99dd0": "Matching_Ethers()", "0x99a5d747": "calculateFee(uint256)", "0x3c67c51e": "testLogs()", "0x12ab7242": "setupStackDepthLib(address)", "0xad9ec17e": "setGreyToken()", "0xc37e8cb2": "testExportAuthorized()", "0x43046844": "placeBet(uint8)", "0xc6e1c178": "TheLuckyOne(bytes)", "0x13d1aa2e": "f(uint256,uint256)", "0x64a4a5d7": "testBitsEqualSuccess()", "0xfb1669ca": "setBalance(uint256)", "0x40fdef80": "administration(uint256,string,uint256,uint256,address)", "0xcf7315c6": "retract(bytes20)", "0x76196c88": "setDnsrr(bytes32,bytes)", "0x08bf2d0d": "getOrderBook(uint256,uint256)", "0x021c309a": "solveBet(address,uint8,bool,uint8)", "0x4de162e4": "extractAccountLength()", "0x56fa47f0": "split(address)", "0xb3a0b1ef": "basicInfoGetter()", "0x26066ad5": "offer(uint256,bytes,uint256,bytes)", "0x99c724ef": "skipInLine(uint256,uint256)", "0x838445e8": "EtherAds(address,address,address)", "0xe06174e4": "settings()", "0xfac5bb92": "getPreRelease(bytes32)", "0x93c94acb": "calculateRewards(uint256[3][3])", "0xd7fa1007": "setHash(bytes32,bytes32)", "0x2a714078": "triggerAuth()", "0x4cd995da": "registerCompany(address,string)", "0xf6469342": "_setPackedBlockNumber(bytes32,uint256)", "0x8e7fd292": "trySetSubnodeOwner(bytes32,address)", "0x4f573cb2": "withdrawRevenue()", "0x924c28c1": "ContractInterface(address,address,address)", "0x4fc9c91a": "identityOf(bytes32)", "0x19901f1d": "TokenSale(uint256,uint256)", "0xaf8b7525": "CollectAndReduceFees(uint256)", "0x3ccb7dc9": "CrowdFund(uint256,uint256)", "0xaeeb96af": "Highlander()", "0xa126c5df": "GAS_TO_AUTHORIZE_EXECUTION()", "0x13c89a8f": "getAllowedTime(bytes32)", "0xf38b0600": "fireEventLog3()", "0xc7144269": "changeSettings_only_Dev(uint256,uint256,uint256,uint256,uint16,uint256,uint256,uint256,uint8,uint8)", "0xefc81a8c": "create()", "0x7429c086": "repeat()", "0x9c0a4bbc": "AlwaysFail()", "0xc3d23e10": "checkBet()", "0x28a45038": "testTryProxyCall()", "0xa668d7c9": "NiceGuyPonzi()", "0x06fe1fd7": "getPackageName(bytes32)", "0x29f27577": "InvestorList(uint256)", "0x57e25a79": "PullPaymentCapable()", "0x6d1669e1": "approveAndCall(address,address,uint256,bytes)", "0xa7e93e87": "retractLatestRevision(bytes20)", "0x9c7e8a03": "addParticipant(address,address,uint256)", "0xc6ab4514": "sendRobust(address,uint256,uint256)", "0xe8f6bc2e": "changeAccountLevelsAddr(address)", "0xb5d1990d": "numRecords()", "0x3e853128": "getGasForXau(address)", "0xa1add510": "hasRelation(bytes32,bytes32,address)", "0x31e3e2fe": "WithDraw()", "0x86723215": "createMarket(bytes,uint256,uint256,address)", "0xce845d1d": "currentBalance()", "0xc3a2c0c3": "scheduleCall()", "0xcf8eeb7e": "subBalance(address,uint256)", "0xaeb4f0d3": "RegisterTwo(address,address)", "0x3c716e08": "updateAuthority(address)", "0x9919b1cc": "getContentsByRanks(address,uint256,uint256,uint256)", "0x0f06670a": "didWin(bytes32)", "0x74e4435f": "getUserAddress(uint256,bytes32)", "0x4664b235": "bytes32_to_bytes(bytes,bytes,bytes)", "0x2ac9bf09": "bid(uint256,uint256,uint256)", "0xf11c4482": "approveFromProxy(address,address,uint256)", "0xfe992c98": "balanceOfAll(address)", "0x43e332c5": "Last_block_number_and_blockhash_used()", "0x0066753e": "removeCertifier(address)", "0xd299dac0": "blake2b(bytes,bytes,uint64)", "0x41395efa": "dgxBalance()", "0xac1b14ff": "proxyCall(uint256)", "0x7a6ce2e1": "getMsgSender()", "0x3855dcd6": "getContrarians_by_index(uint256)", "0xe6febc9b": "investorWithdraw(uint256)", "0xe6e91cfc": "voidFailedPayment(uint256)", "0x547eeac1": "acceptTransfer()", "0x9824425a": "takeOrder(uint256,uint256,uint256,uint256)", "0xdf25ee23": "getIndexId(address,bytes)", "0x0f3d7c3e": "release(string,uint32[3],string,string,string)", "0x15cff546": "isOperationBlocked()", "0x0b927666": "order(address,uint256,address,uint256,uint256,uint256)", "0x00ce2057": "triggerPayment()", "0x9a9c29f6": "settle(uint256,uint256)", "0x0f096163": "Chainy()", "0x2f5a5c5b": "timegame()", "0x900d85fa": "updatePreReleaseTree(bytes32)", "0x1cbd0519": "accountLevel(address)", "0x29a065bd": "getLOg(uint256)", "0xcec95aa1": "getReleaseHashForPackage(string,uint256)", "0x41524433": "sellKissBTCWithCallback(uint256,address,uint256)", "0x5e431709": "sealedBids(address,bytes32)", "0xf55b23c0": "externalLeave()", "0x31375242": "ownerSetTreasury(address)", "0x51b42b00": "deactivate()", "0x5af36e3e": "refund(uint256,uint256)", "0xc5096a69": "feeFor(address,address,uint256)", "0x059a500c": "makeDeposit(uint256)", "0x3c2e7d54": "priv_inMainChain__(int256,int256)", "0x9431f5f0": "withdrawFees(bytes)", "0x91b4a0e7": "Difficulty()", "0x268d50fe": "ownerSetHouseEdge(uint256)", "0x9644fcbd": "changeMembership(address,bool,string)", "0x66aa6f26": "payFee(bytes)", "0x353928d8": "helpRed()", "0x9c1193ea": "GreeterA(bytes)", "0xdd79e33e": "splitIdentifiers(string)", "0x4d268ddd": "payImporterBankForGoodsBought()", "0x656d2f63": "ManagedAccount(address)", "0x1216e771": "expiration(uint64)", "0x36f7cd70": "setPricePerStake(uint256)", "0x7842a3a4": "payReward()", "0x0ae50a39": "GetOwner()", "0xc81caae7": "acceptMember(address,string,string)", "0xe50dce71": "testControllerApproveSetsAllowance()", "0x4b64e492": "execute(address)", "0xd9e947f3": "kickOutMember(address)", "0x35cc59a9": "createSchema(bytes)", "0x2530c905": "rand(uint256)", "0x4894e37f": "__callback(bytes,string,bytes)", "0x70480275": "addAdmin(address)", "0x969cb7c3": "getPublisher(uint256)", "0x4ed4831a": "all(bool[7])", "0x2ef3accc": "getPrice(string,uint256)", "0x67854643": "getGenerationMemberLength(uint256)", "0xe6690fb1": "nextAuction(uint256)", "0x5829d310": "entries(int256)", "0x7fe1dc7e": "getToken(bytes)", "0xe7329e71": "scheduleCall(bytes,bytes,uint256,uint256,uint8,uint256)", "0x41c12a70": "voteNo()", "0x6a28db13": "getQrLength()", "0xdd93890b": "setMeta(uint256,bytes32,bytes32)", "0xa48bdb7c": "results()", "0x9d888e86": "currentVersion()", "0xff81fb91": "unhint(int256,bytes32)", "0x9ec32d45": "challengeWinningOutcome(bytes,uint16)", "0xa0a2f629": "setReferralId(uint256,address)", "0x76577eae": "distributeEarnings()", "0x3e5cee05": "issueIOU(string,uint256,address)", "0xf3c7d275": "prenup(string,string,string,string,string,address,address)", "0x7154ae61": "CheckNumbers(uint8[5])", "0x05de4f07": "getContentParent(uint256)", "0xb81e43fc": "getEventName()", "0xa7eeea37": "NewContributor(uint256)", "0xe816a515": "takeFlight()", "0x05b2b03a": "CertificationCentre(address)", "0x74d4ab27": "fipsRegister()", "0x65fa2f7f": "getLastPrice(uint256)", "0xcc8b34ab": "CrowdCoin()", "0xe2b178a0": "getAuthority()", "0x5fb64fd6": "checkMembership(address)", "0x7948f523": "setAmbiAddress(address,bytes32)", "0xebb71194": "withdrawFees(bytes32)", "0x6545bed3": "Dice(uint256,uint256,uint256,uint256,uint256,uint256,uint256)", "0x7065cb48": "addOwner(address)", "0x913f424c": "_ecMul(uint256,uint256,uint256,uint256)", "0xdbecc372": "Example(uint256)", "0x9f7f760c": "SimpleDice()", "0xca94692d": "abiSignature()", "0x61ba3377": "WatchLastTime()", "0x20e647e1": "checkBetColor(uint8,address,bytes32,bytes32)", "0x0a3b0a4f": "add(address)", "0xc51cf179": "calcBaseFeeForShares(uint256)", "0x8baced64": "isInPool(address)", "0x4dc415de": "reject()", "0x1555e337": "ConferenceCertificate()", "0x9555a942": "withdrawFrom(address,address,uint256)", "0xe1efda6d": "airaSend(address,address,uint256)", "0x8bfc2f33": "delegateDAOTokens(uint256)", "0xb964608d": "get_return_by_level(uint256)", "0x0c1fad51": "setSeedSourceA(address)", "0x98688a95": "Ai()", "0xd930a90b": "testFailMoveBalanceDueToInsufficientFunds()", "0x337b5988": "testSimpleNameRegister()", "0xf06d335e": "_recoverAccount(address,address)", "0x025bbbe5": "newSale(bytes16,uint256,uint256)", "0x984413b8": "_eraseNode(bytes32)", "0x5ea187c9": "BuildByteArray(bytes)", "0xc2a95cc9": "updateTrustSettings(address,uint256)", "0xde0ff7c5": "getEther()", "0x4ac7becf": "SimpleSign()", "0x252786e4": "WatchBlockSizeInEther()", "0xf2016a4f": "updateMinEthPerNotification(uint256)", "0xd743ca38": "newWinner(uint256,address,uint256,uint256,uint256)", "0x9eab5253": "getMembers()", "0x51d38d5f": "addDeveloper(address,string)", "0x930ed251": "getSavedVar()", "0x715ef4ff": "resendFailedPayment(uint256)", "0xb1418cf4": "payHouse()", "0xe1569f6b": "testThrowsSetNotRetractableNotOwner()", "0x4ae9af61": "getBotStats(uint256,uint256)", "0xbf8ecf9c": "authProposals()", "0xc00ca383": "getByOwner(address,uint256)", "0xc8e7ca2e": "getMsgData()", "0x711953ef": "setGameAddress(address)", "0x63a8dac2": "changeSettings(uint256,uint256,uint256,uint8,uint256,uint256,uint8,uint8)", "0x72c87075": "testBlockHeaderFetch()", "0x4c7a2254": "checkMyWithdraw()", "0xab91c7b0": "queueLength()", "0x25209260": "PrepareRoll(uint256)", "0x58150c8b": "GameRegistry()", "0x75608264": "get_hash(uint8,bytes32)", "0x6510ef4d": "oraclize_query(uint256,string,string[5])", "0xd57a12f5": "testCheckSigs()", "0x3f415772": "releaseExists(bytes32)", "0xda25c0cd": "ThisExternalAssembly()", "0xf239e528": "sendOneEtherHome()", "0xc4321adb": "investInTheSystem(uint256)", "0x4fab2ca4": "testGetFrontend()", "0x05261aea": "finalize(uint256)", "0x576eac66": "setFundingGoal(uint256)", "0xe75528cc": "buyBuilding(uint256,uint256)", "0x6ed43eb0": "getInvestorList(uint256)", "0xb38415f3": "getConfigBytes(bytes)", "0x771ad635": "getContentCred(address,uint256)", "0x93c166ec": "computeEndowment(uint256,uint256,uint256,uint256)", "0xac35caee": "transferWithReference(address,uint256,string)", "0xc6803622": "wasCalled()", "0x8cfd8901": "_incBlock()", "0xfcf0f55b": "eventOracles(bytes32,uint256)", "0x505ff574": "register(address,uint256,bool)", "0xf824384a": "addInvestorAtID(uint256)", "0x6b9f96ea": "flush()", "0xc3d0a564": "getAccountBalance(bytes)", "0x30fd300f": "registerBytes32(address,bytes32)", "0xc3169ef2": "respond(uint256,uint256[4])", "0xcf1cd249": "secureSend(address)", "0x62c335c1": "checkCallback(address,uint256,bytes,bytes)", "0xb599afc8": "totalBetCount()", "0x69433e12": "setExchange(uint256)", "0x899942b8": "Devcon2Token()", "0x4c2d71b3": "setConfigAddress(bytes32,address)", "0xb974b0a3": "allData()", "0x27e8c2d8": "burnUnicornShares()", "0xf639365d": "testSetGet()", "0x2f5d3916": "testControllerApproveTriggersEvent()", "0x938b5f32": "origin()", "0xd60dcb5d": "Switch()", "0xde629235": "getCertificationDocumentAtIndex(address,uint256)", "0x329ce29e": "buyTile(uint256)", "0x59e2d30e": "testThrowBlobStoreNotRegistered()", "0xa005b87b": "NullMapTest()", "0xc13afa91": "object_locations(uint256)", "0x4848b1a5": "setData(uint256,uint256)", "0x80ede329": "getDocumentDetails(uint256)", "0x35d13969": "SendAllMoney()", "0x8040cac4": "testOverflow()", "0x9507d39a": "get(uint256)", "0xc040e6b8": "stage()", "0x18178358": "poke()", "0xfd782de5": "Proxy()", "0xfd68a422": "returnmoneycreator(uint8,uint128)", "0x86a50535": "voteFor(uint256)", "0x44602a7d": "testFallbackReturn()", "0xa230c524": "isMember(address)", "0x3ffbd47f": "register(string,string)", "0x8cecf66e": "_inverse(uint256)", "0x51017702": "isOutcomeSet(bytes32)", "0xd408746a": "GetContractAddr()", "0x20130753": "testThrowSetNotRetractableNotOwner()", "0xa0e2abf7": "getFirstActiveGamble()", "0x7c582304": "updateInvestmentTotal(address,uint256)", "0x95d89b41": "symbol()", "0x1768b436": "ETCSurvey()", "0x6d4ce63c": "get()", "0xc41a360a": "getOwner(uint256)", "0x49942ccb": "scheduleCall(bytes,bytes,uint256,uint256)", "0x6b64c769": "startAuction()", "0x084d72f4": "getWinningOutcome(uint256)", "0xd379be23": "claimer()", "0x41fa4876": "multiBlockRandomGen(uint256,uint256)", "0x5bc7e259": "updateRelease(uint32,uint32,uint32,bytes,bool)", "0x47f3d794": "configure(uint256,uint8,uint256,uint256,uint256,uint256)", "0xe2bbb158": "deposit(uint256,uint256)", "0x953a7fab": "testMoveBalance()", "0xeacc5b3b": "safeSend(address,uint256,uint256)", "0x96f0aa8f": "findNextSecond(uint256,bytes)", "0xc8690233": "pubkey(bytes32)", "0x459f93f7": "getBuyers(uint256,address)", "0xf714de9c": "MultiAccess()", "0xf4a81d08": "getKudosGiven(address)", "0x5aa94a68": "computeResultVoteExtraInvestFeesRate()", "0xdb2a0cb7": "HumanStandardTokenFactory()", "0xdf3a6b10": "testMemberAddedEvent()", "0xce8d054e": "_setupNoCallback()", "0x8ea822d8": "createThings(bytes32[],uint16[],bytes32[],uint16[],uint88)", "0x24fb563f": "PlayerTickets(address,uint256,uint256)", "0x8c0e156d": "scheduleCall(bytes4,uint256,uint256)", "0xef04fdb7": "buyShares(bytes,uint8,uint256,uint256)", "0xa0afd731": "dividendBalance(address)", "0xc3c95c7b": "getMarket(bytes32)", "0x94ed9b77": "append(address,address)", "0xc87b36ed": "disableBetting()", "0x566735d8": "PreVNK(uint256,string,string,uint8)", "0x400aae08": "isInCurrentGeneration(address)", "0x44dd4b5e": "scheduleTransaction(address,uint256,bytes)", "0x48c54b9d": "claimTokens()", "0xe8930efd": "Investors(address)", "0xa6f2ae3a": "buy()", "0x12819817": "setXauForGasCurrator(address)", "0x056e1059": "oraclize_query(uint256,string,string,uint256)", "0x7824407f": "tokenSupply()", "0x7f0c949c": "setJurisdication(string)", "0x2e817963": "set_sdl(address)", "0xaee84f6b": "setTime(address,uint256)", "0x3c0dde1c": "_addPools(address,address)", "0xf8bd526e": "setCoinageContract(address)", "0x04b07a5e": "removeUpdater(address)", "0x11149ada": "getProof(uint256)", "0x4306cc3f": "queryEarnings(address)", "0x55241077": "setValue(uint256)", "0x492b67ea": "Etherdoc()", "0xadf59f99": "query(uint256,string,string)", "0x951b01c5": "setCertifierDb(address)", "0x8ae986cf": "registrantApprove(address)", "0xfa68b4ce": "lookupISO3116_1_alpha_3(bytes)", "0x7fdc8290": "isUnderscore(bytes1)", "0x89495172": "convictFinal(uint256,uint256)", "0x93e02d13": "FallenLeaders()", "0x3e476053": "moveFunds(address,uint256)", "0x8894dd2b": "addEther()", "0x8f03850b": "numContributors()", "0xbfc3cd2f": "testFailChargeMoreThanApproved()", "0x1d82e9c7": "EXTRA_GAS()", "0x278ecde1": "refund(uint256)", "0x0f825673": "deleteCoupon(string)", "0xa324ad24": "getMonth(uint256)", "0xd628e0a6": "WatchBalance()", "0xb238ad0e": "getDaysInMonth(uint8,uint16)", "0xd6febde8": "buy(uint256,uint256)", "0x370ec1c5": "_fillOrder(address,uint256)", "0x4c33fe94": "cancel(address)", "0xcdd13701": "getEventHashes(uint256[256])", "0xe1bc3003": "reveal(bytes,string)", "0xa2e62045": "update()", "0x75f45878": "scheduleCall(bytes,bytes,uint256)", "0xd2756e11": "finalizeNumber(uint256)", "0x48519189": "MonedaAlcala(string,string)", "0x009b9369": "getVoteNumber(uint256)", "0xdaa283c8": "__callback(bytes,string)", "0xfcce2622": "challengeAnswer(uint256,bytes)", "0xac18de43": "removeManager(address)", "0x16d9356f": "oraclize_query(string,string[4])", "0xd1734eac": "isInNextGeneration(address)", "0x524fa7b9": "whitelistAdd(address)", "0xa5eb7a4e": "operated()", "0xb0aab296": "getNextNode(bytes)", "0x1982ed58": "ChangeReuseCashInHarware(bool,uint16,uint16)", "0xdf811d7d": "numberOfPlayersInCurrentRound()", "0xca7dc5b1": "getNumberOfTweets()", "0x488b3538": "shares(address,bytes32,int256)", "0xebd83378": "get_blocks_for(uint256)", "0x399fdb86": "testFailNormalWhitelistReset()", "0xca0c1e62": "computeMerkle(int256,int256,int256[],int256,int256,int256[])", "0x8963dab4": "getNodeId(bytes,bytes)", "0x7d94792a": "seed()", "0xbcf175c8": "oraclize_cbAddress()", "0x38eee93e": "scheduleCall(address,bytes,bytes,uint16,uint8,uint256[5])", "0xa08d3f83": "Etheropt(uint256,string,uint256,uint256,bytes32,address,int256[])", "0xae47a290": "changeMaxBet(uint256)", "0xd12c1e28": "badgesOf(address)", "0x001f8d11": "removePackage(bytes32,string)", "0x54fd4d50": "version()", "0x89abeb19": "ProcessGameExt(uint256)", "0x3dd7c1b9": "newProduct(string,string,uint256,uint256)", "0xa396541e": "getPongvalTxRetrievalAttempted()", "0xcc8af0fe": "bytesToUInt(bytes,bytes)", "0x983b94fb": "finalizeAuction(bytes32)", "0x3df91162": "getUpdatable(bytes20)", "0x045236b4": "getChainyData(string)", "0x9c172f87": "EthVentures4()", "0x996a4be3": "uintToBytes(uint256,uint256)", "0x775a8f5e": "toBytes(uint256)", "0xb6db75a0": "isAdmin()", "0x0b6fcdb0": "getEnforceRevisions(bytes32)", "0x29d6f899": "BetOnBlue()", "0xe6cbcba9": "PlusOnePonzi()", "0xc9030ea0": "addMember(address,bool)", "0x8f283970": "changeAdmin(address)", "0x670c884e": "setup(address,uint256,uint256,uint256,address)", "0x808ab1d6": "getCertificationDbCount()", "0x018f5472": "isAUser(address)", "0x59c87d70": "request(bytes32)", "0x407cfe5e": "get_all_players()", "0x33f472b9": "MPO()", "0x662dbe96": "getNodeHeight(bytes)", "0x60b1e173": "getProof(uint256,address,address)", "0xf25eb5c1": "removeReverse()", "0x1d065dde": "_transferWithReward(address,address,uint256)", "0x65343fcb": "TrustEth()", "0xaa237e21": "set(bool,uint256)", "0x60e519c0": "computeMarginAmount()", "0xd9597016": "multisetCustomGasPrice(uint256[],address[])", "0x4f10acc1": "updateGoldFeeData(uint256)", "0x1e9ea66a": "balanceEther10000000(uint256)", "0xffe34512": "getNumChannels(address)", "0x71dd8862": "IndexOf()", "0xdd9dd688": "calcStopPrice()", "0x934354e7": "finishSpin()", "0x26881518": "setupFee(address)", "0x5eb3f639": "assertTrue(bool,bytes)", "0xe9540395": "getRewardDivisor()", "0x8e4afa51": "checkTransferToICAP(bytes32,uint256)", "0xb5b33eda": "scheduleCall(address,uint256)", "0x3d6a32bd": "createTradeContract(address,uint256,uint256,uint256,bool,bool)", "0x5fd9dff6": "allowance(address,address,bytes)", "0x0d244d68": "setNotRetractable(bytes32)", "0xe63697c8": "withdraw(uint256,address,uint256)", "0x3e5087cc": "testBasicThing()", "0xee77fe86": "scheduleCall(address,bytes4,bytes,uint256,uint256,uint8)", "0xb1cc4348": "placeWager()", "0xb95594e5": "lineOfPlayers(uint256)", "0xa9cc4718": "fail()", "0x54385526": "setStatus(uint8,uint8,string)", "0xb45c48dc": "Security_AddPasswordSha3HashToBankAccount(bytes)", "0xace51abc": "helperVerifyHash__(uint256,int256,int256[],int256,uint256,int256,int256[],int256)", "0x1f5d0b4c": "address(address,address,uint256)", "0x7acbfb65": "setOwner(uint256,uint256)", "0x3462f32d": "execWithGasLimit(bytes32,bytes32,uint256,uint256)", "0xac04f5a7": "append(address)", "0x2fcb6628": "_stringGas(string,string)", "0xe977992d": "Doubler()", "0xc57a050e": "fairandeasy()", "0x412664ae": "sendToken(address,uint256)", "0x0afa9fb9": "contains(int256,address)", "0xb69ef8a8": "balance()", "0x264c8e9a": "whatWasTheVal()", "0x255016c8": "checkIfExploded()", "0xd716222c": "is_owner(uint256,address)", "0xc398f030": "expire(uint256,uint8,bytes,bytes,bytes)", "0xb7d454a4": "setNotTransferable(bytes32)", "0x4789aaef": "EthereumDice()", "0xc0171112": "timestamp(uint64)", "0x4f60f334": "multiAccessAddOwner(address)", "0x80aed05f": "LooneyDice()", "0x55ba343f": "getMarket(bytes)", "0x943b0747": "RewardOffer(address,address,bytes,uint256,uint256,uint128,uint256)", "0xa27c672a": "owner_reveal_and_commit(uint8,bytes32,bytes32)", "0x8f731077": "extractAllowanceRecordLength(address)", "0xc3d345c4": "getHangoutAddress()", "0xa8978434": "softResolveAnswer(uint256)", "0x7cef6047": "getNavHistory(uint256)", "0xfae14192": "changeFeePercentage(uint256)", "0x2ddbc04a": "play2(address,uint256)", "0x3fb27b85": "seal()", "0xe8038e25": "TokenSale(uint256,uint256,address)", "0x8d92fdf3": "withdrawAsset(uint256)", "0x8579cbde": "getPrice(string,uint256,address)", "0x0d61b519": "executeProposal(uint256)", "0x63a599a4": "emergencyStop()", "0x661e3605": "ConstructorContract(uint256)", "0xfd7c460d": "ciberLottery()", "0x1f83f440": "getPaymentByAddress(address)", "0xdcf73856": "generateGroups()", "0x3c6e03d7": "thewhalegame()", "0x271cd760": "getPackageDb()", "0x53fefd7d": "changeMaxDeposit(uint256)", "0xae169a50": "claimReward(uint256)", "0x6da84ec0": "calcMarketFee(bytes32,uint256)", "0xb16562fe": "fipsRegister(address,bytes)", "0x041fe13d": "onEtherandomSeed(bytes32,bytes32)", "0x4a617faa": "shaBid(bytes32,uint256,bytes32)", "0x052b2aa7": "getRegistrants()", "0x0ff0a4df": "reFund()", "0xe56b9dce": "GetPrize(uint256)", "0x8eec99c8": "setNewAdmin(address)", "0xffcce369": "changeIPFSHash(string)", "0x40fdf515": "issuetender(address,uint256,uint256)", "0xef4bdfdd": "Set_your_game_number_between_1_15(string)", "0xa991cb0e": "respond(uint256)", "0x617fba04": "getRecord(address)", "0x475a9fa9": "issueTokens(address,uint256)", "0xd30fbd0d": "safeSubtract(uint256,uint256)", "0xe54d4051": "receiveInteger(bytes,uint256,uint16)", "0x8023ffbd": "getOverallSize()", "0x8390b02a": "rfindPtr(uint256,uint256,uint256,uint256)", "0x11e99c22": "arrival()", "0x10c1952f": "setLocked()", "0x039a21b8": "tryExecute(address,bytes,uint256)", "0x201dcd7a": "newChallenge(uint256,uint256)", "0x5aebfd14": "createFile(bytes)", "0xa6b1caa3": "gasScalar(uint256)", "0x200538c6": "DTE()", "0xd1738b72": "wroomWroom()", "0x22f607f6": "Escrow()", "0x7e81b6aa": "KingdomFactory()", "0x901d7775": "voteOutMasterKey(address)", "0xd6af9411": "Rouleth()", "0x7b0383b2": "initializeDispute(uint256)", "0x70c9edb7": "BTCRelayTools(address)", "0xe9c31315": "checkBetParity(uint8,address,bytes32,bytes32)", "0xee82ac5e": "getBlockHash(uint256)", "0x727089f1": "extractAllowanceLength()", "0x8bbb5af7": "test1Fails()", "0x47872b42": "unsealBid(bytes32,uint256,bytes32)", "0x46b04e53": "PlayerInfoPerZone(uint256,uint256)", "0x752bacce": "getExecPrice()", "0x89d8ca67": "drawPot(bytes32,bytes32)", "0xc23697a8": "check(address)", "0x0af658ca": "personUpdateActivity(uint256,bool)", "0x24d4e90a": "ln(uint256)", "0x09d33f1d": "addRequest(address,uint256)", "0xc913b552": "getVersions(bytes)", "0xbb3b8dca": "getCertificateHash(bytes)", "0x3809c0bf": "doInfinite()", "0x853552d7": "_slotAddNew(address)", "0xccf1ab9b": "usurpation()", "0xe7dafdb6": "transfer_token(address,address,uint256)", "0x0c77a697": "claimFounders()", "0xda82a035": "sweepCommission()"} \ No newline at end of file diff --git a/cmd/clef/README.md b/cmd/clef/README.md new file mode 100644 index 0000000000..93799a7610 --- /dev/null +++ b/cmd/clef/README.md @@ -0,0 +1,864 @@ +Clef +---- +Clef can be used to sign transactions and data and is meant as a replacement for geth's account management. +This allows DApps not to depend on geth's account management. When a DApp wants to sign data it can send the data to +the signer, the signer will then provide the user with context and asks the user for permission to sign the data. If +the users grants the signing request the signer will send the signature back to the DApp. + +This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can +help in situations when a DApp is connected to a remote node because a local Ethereum node is not available, not +synchronised with the chain or a particular Ethereum node that has no built-in (or limited) account management. + +Clef can run as a daemon on the same machine, or off a usb-stick like [usb armory](https://inversepath.com/usbarmory), +or a separate VM in a [QubesOS](https://www.qubes-os.org/) type os setup. + + +## Command line flags +Clef accepts the following command line options: +``` +COMMANDS: + init Initialize the signer, generate secret storage + attest Attest that a js-file is to be used + addpw Store a credential for a keystore file + help Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --loglevel value log level to emit to the screen (default: 4) + --keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore") + --configdir value Directory for clef configuration (default: "$HOME/.clef") + --networkid value Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby) (default: 1) + --lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength + --nousb Disables monitoring for and managing USB hardware wallets + --rpcaddr value HTTP-RPC server listening interface (default: "localhost") + --rpcport value HTTP-RPC server listening port (default: 8550) + --signersecret value A file containing the password used to encrypt signer credentials, e.g. keystore credentials and ruleset hash + --4bytedb value File containing 4byte-identifiers (default: "./4byte.json") + --4bytedb-custom value File used for writing new 4byte-identifiers submitted via API (default: "./4byte-custom.json") + --auditlog value File used to emit audit logs. Set to "" to disable (default: "audit.log") + --rules value Enable rule-engine (default: "rules.json") + --stdio-ui Use STDIN/STDOUT as a channel for an external UI. This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user interface, and can be used when the signer is started by an external process. + --stdio-ui-test Mechanism to test interface between signer and UI. Requires 'stdio-ui'. + --help, -h show help + --version, -v print the version + +``` + + +Example: +``` +signer -keystore /my/keystore -chainid 4 +``` + +Check out the [tutorial](tutorial.md) for some concrete examples on how the signer works. + +## Security model + +The security model of the signer is as follows: + +* One critical component (the signer binary / daemon) is responsible for handling cryptographic operations: signing, private keys, encryption/decryption of keystore files. +* The signer binary has a well-defined 'external' API. +* The 'external' API is considered UNTRUSTED. +* The signer binary also communicates with whatever process that invoked the binary, via stdin/stdout. + * This channel is considered 'trusted'. Over this channel, approvals and passwords are communicated. + +The general flow for signing a transaction using e.g. geth is as follows: +![image](sign_flow.png) + +In this case, `geth` would be started with `--externalsigner=http://localhost:8550` and would relay requests to `eth.sendTransaction`. + +## TODOs + +Some snags and todos + +* [ ] The signer should take a startup param "--no-change", for UIs that do not contain the capability + to perform changes to things, only approve/deny. Such a UI should be able to start the signer in + a more secure mode by telling it that it only wants approve/deny capabilities. + +* [x] It would be nice if the signer could collect new 4byte-id:s/method selectors, and have a +secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for +inclusion upstream. + +* It should be possible to configure the signer to check if an account is indeed known to it, before +passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate +accounts if it immediately returned "unknown account". +* [x] It should be possible to configure the signer to auto-allow listing (certain) accounts, instead of asking every time. +* [x] Done Upon startup, the signer should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode), +invoking methods with the following info: + * [x] Version info about the signer + * [x] Address of API (http/ipc) + * [ ] List of known accounts +* [ ] Have a default timeout on signing operations, so that if the user has not answered withing e.g. 60 seconds, the request is rejected. +* [ ] `account_signRawTransaction` +* [ ] `account_bulkSignTransactions([] transactions)` should + * only exist if enabled via config/flag + * only allow non-data-sending transactions + * all txs must use the same `from`-account + * let the user confirm, showing + * the total amount + * the number of unique recipients + +* Geth todos + - The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is +put together is a bit of a hack into the http server. This could probably be greatly improved + - Relay: Geth should be started in `geth --external_signer localhost:8550`. + - Currently, the Geth APIs use `common.Address` in the arguments to transaction submission (e.g `to` field). This + type is 20 `bytes`, and is incapable of carrying checksum information. The signer uses `common.MixedcaseAddress`, which + retains the original input. + - The Geth api should switch to use the same type, and relay `to`-account verbatim to the external api. + +* [x] Storage + * [x] An encrypted key-value storage should be implemented + * See [rules.md](rules.md) for more info about this. + +* Another potential thing to introduce is pairing. + * To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API). + * Thus geth/mist/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests. + * This feature would make the addition of rules less dangerous. + +* Wallets / accounts. Add API methods for wallets. + +## Communication + +### External API + +The signer listens to HTTP requests on `rpcaddr`:`rpcport`, with the same JSONRPC standard as Geth. The messages are +expected to be JSON [jsonrpc 2.0 standard](http://www.jsonrpc.org/specification). + +Some of these call can require user interaction. Clients must be aware that responses +may be delayed significanlty or may never be received if a users decides to ignore the confirmation request. + +The External API is **untrusted** : it does not accept credentials over this api, nor does it expect +that requests have any authority. + +### UI API + +The signer has one native console-based UI, for operation without any standalone tools. +However, there is also an API to communicate with an external UI. To enable that UI, +the signer needs to be executed with the `--stdio-ui` option, which allocates the +`stdin`/`stdout` for the UI-api. + +An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`. + +The model is as follows: + +* The user starts the UI app (`pythonsigner.py`). +* The UI app starts the `signer` with `--stdio-ui`, and listens to the +process output for confirmation-requests. +* The `signer` opens the external http api. +* When the `signer` receives requests, it sends a `jsonrpc` request via `stdout`. +* The UI app prompts the user accordingly, and responds to the `signer` +* The `signer` signs (or not), and responds to the original request. + +## External API + +See the [external api changelog](extapi_changelog.md) for information about changes to this API. + +### Encoding +- number: positive integers that are hex encoded +- data: hex encoded data +- string: ASCII string + +All hex encoded values must be prefixed with `0x`. + +## Methods + +### account_new + +#### Create new password protected account + +The signer will generate a new private key, encrypts it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and stores it in the keystore directory. +The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts. + +#### Arguments + +None + +#### Result + - address [string]: account address that is derived from the generated key + - url [string]: location of the keyfile + +#### Sample call +```json +{ + "id": 0, + "jsonrpc": "2.0", + "method": "account_new", + "params": [] +} + +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133", + "url": "keystore:///my/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133" + } +} +``` + +### account_list + +#### List available accounts + List all accounts that this signer currently manages + +#### Arguments + +None + +#### Result + - array with account records: + - account.address [string]: account address that is derived from the generated key + - account.type [string]: type of the + - account.url [string]: location of the account + +#### Sample call +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "account_list" +} + +{ + "id": 1, + "jsonrpc": "2.0", + "result": [ + { + "address": "0xafb2f771f58513609765698f65d3f2f0224a956f", + "type": "account", + "url": "keystore:///tmp/keystore/UTC--2017-08-24T07-26-47.162109726Z--afb2f771f58513609765698f65d3f2f0224a956f" + }, + { + "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133", + "type": "account", + "url": "keystore:///tmp/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133" + } + ] +} +``` + +### account_signTransaction + +#### Sign transactions + Signs a transactions and responds with the signed transaction in RLP encoded form. + +#### Arguments + 2. transaction object: + - `from` [address]: account to send the transaction from + - `to` [address]: receiver account. If omitted or `0x`, will cause contract creation. + - `gas` [number]: maximum amount of gas to burn + - `gasPrice` [number]: gas price + - `value` [number:optional]: amount of Wei to send with the transaction + - `data` [data:optional]: input data + - `nonce` [number]: account nonce + 3. method signature [string:optional] + - The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected. + + +#### Result + - signed transaction in RLP encoded form [data] + +#### Sample call +```json +{ + "id": 2, + "jsonrpc": "2.0", + "method": "account_signTransaction", + "params": [ + { + "from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", + "gas": "0x55555", + "gasPrice": "0x1234", + "input": "0xabcd", + "nonce": "0x0", + "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "value": "0x1234" + } + ] +} +``` +Response + +```json +{ + "jsonrpc": "2.0", + "id": 67, + "error": { + "code": -32000, + "message": "Request denied" + } +} +``` +#### Sample call with ABI-data + + +```json +{ + "jsonrpc": "2.0", + "method": "account_signTransaction", + "params": [ + { + "from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "gas": "0x333", + "gasPrice": "0x1", + "nonce": "0x0", + "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "value": "0x0", + "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" + }, + "safeSend(address)" + ], + "id": 67 +} +``` +Response + +```json +{ + "jsonrpc": "2.0", + "id": 67, + "result": { + "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", + "tx": { + "nonce": "0x0", + "gasPrice": "0x1", + "gas": "0x333", + "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "value": "0x0", + "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", + "v": "0x26", + "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", + "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", + "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" + } + } +} +``` + +Bash example: +```bash +#curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ + +{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}} +``` + + +### account_sign + +#### Sign data + Signs a chunk of data and returns the calculated signature. + +#### Arguments + - account [address]: account to sign with + - data [data]: data to sign + +#### Result + - calculated signature [data] + +#### Sample call +```json +{ + "id": 3, + "jsonrpc": "2.0", + "method": "account_sign", + "params": [ + "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", + "0xaabbccdd" + ] +} +``` +Response + +```json +{ + "id": 3, + "jsonrpc": "2.0", + "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" +} +``` + +### account_ecRecover + +#### Recover address + Derive the address from the account that was used to sign data from the data and signature. + +#### Arguments + - data [data]: data that was signed + - signature [data]: the signature to verify + +#### Result + - derived account [address] + +#### Sample call +```json +{ + "id": 4, + "jsonrpc": "2.0", + "method": "account_ecRecover", + "params": [ + "0xaabbccdd", + "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" + ] +} +``` +Response + +```json +{ + "id": 4, + "jsonrpc": "2.0", + "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db" +} + +``` + +### account_import + +#### Import account + Import a private key into the keystore. The imported key is expected to be encrypted according to the web3 keystore + format. + +#### Arguments + - account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export) + +#### Result + - imported key [object]: + - key.address [address]: address of the imported key + - key.type [string]: type of the account + - key.url [string]: key URL + +#### Sample call +```json +{ + "id": 6, + "jsonrpc": "2.0", + "method": "account_import", + "params": [ + { + "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "401c39a7c7af0388491c3d3ecb39f532" + }, + "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a" + }, + "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806" + }, + "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9", + "version": 3 + }, + ] +} +``` +Response + +```json +{ + "id": 6, + "jsonrpc": "2.0", + "result": { + "address": "0xc7412fc59930fd90099c917a50e5f11d0934b2f5", + "type": "account", + "url": "keystore:///tmp/keystore/UTC--2017-08-24T11-00-42.032024108Z--c7412fc59930fd90099c917a50e5f11d0934b2f5" + } +} +``` + +### account_export + +#### Export account from keystore + Export a private key from the keystore. The exported private key is encrypted with the original passphrase. When the + key is imported later this passphrase is required. + +#### Arguments + - account [address]: export private key that is associated with this account + +#### Result + - exported key, see [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for + more information + +#### Sample call +```json +{ + "id": 5, + "jsonrpc": "2.0", + "method": "account_export", + "params": [ + "0xc7412fc59930fd90099c917a50e5f11d0934b2f5" + ] +} +``` +Response + +```json +{ + "id": 5, + "jsonrpc": "2.0", + "result": { + "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "401c39a7c7af0388491c3d3ecb39f532" + }, + "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a" + }, + "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806" + }, + "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9", + "version": 3 + } +} +``` + + + +## UI API + +These methods needs to be implemented by a UI listener. + +By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with +denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented. +See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'. + +All methods in this API uses object-based parameters, so that there can be no mixups of parameters: each piece of data is accessed by key. + +See the [ui api changelog](intapi_changelog.md) for information about changes to this API. + +OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line. +Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make +things simpler for both parties. + +### ApproveTx + +Invoked when there's a transaction for approval. + + +#### Sample call + +Here's a method invocation: +```bash + +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ +``` + +```json + +{ + "jsonrpc": "2.0", + "id": 1, + "method": "ApproveTx", + "params": [ + { + "transaction": { + "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "gas": "0x333", + "gasPrice": "0x1", + "value": "0x0", + "nonce": "0x0", + "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", + "input": null + }, + "call_info": [ + { + "type": "WARNING", + "message": "Invalid checksum on to-address" + }, + { + "type": "Info", + "message": "safeSend(address: 0x0000000000000000000000000000000000000012)" + } + ], + "meta": { + "remote": "127.0.0.1:48486", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + } + ] +} + +``` + +The same method invocation, but with invalid data: +```bash + +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000002000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ +``` + +```json + +{ + "jsonrpc": "2.0", + "id": 1, + "method": "ApproveTx", + "params": [ + { + "transaction": { + "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "gas": "0x333", + "gasPrice": "0x1", + "value": "0x0", + "nonce": "0x0", + "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012", + "input": null + }, + "call_info": [ + { + "type": "WARNING", + "message": "Invalid checksum on to-address" + }, + { + "type": "WARNING", + "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)" + } + ], + "meta": { + "remote": "127.0.0.1:48492", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + } + ] +} + + +``` + +One which has missing `to`, but with no `data`: + + +```json + +{ + "jsonrpc": "2.0", + "id": 3, + "method": "ApproveTx", + "params": [ + { + "transaction": { + "from": "", + "to": null, + "gas": "0x0", + "gasPrice": "0x0", + "value": "0x0", + "nonce": "0x0", + "data": null, + "input": null + }, + "call_info": [ + { + "type": "CRITICAL", + "message": "Tx will create contract with empty code!" + } + ], + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] +} +``` + +### ApproveExport + +Invoked when a request to export an account has been made. + +#### Sample call + +```json + +{ + "jsonrpc": "2.0", + "id": 7, + "method": "ApproveExport", + "params": [ + { + "address": "0x0000000000000000000000000000000000000000", + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] +} + +``` + +### ApproveListing + +Invoked when a request for account listing has been made. + +#### Sample call + +```json + +{ + "jsonrpc": "2.0", + "id": 5, + "method": "ApproveListing", + "params": [ + { + "accounts": [ + { + "type": "Account", + "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42", + "address": "0x123409812340981234098123409812deadbeef42" + }, + { + "type": "Account", + "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42", + "address": "0xcafebabedeadbeef34098123409812deadbeef42" + } + ], + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] +} + +``` + + +### ApproveSignData + +#### Sample call + +```json +{ + "jsonrpc": "2.0", + "id": 4, + "method": "ApproveSignData", + "params": [ + { + "address": "0x123409812340981234098123409812deadbeef42", + "raw_data": "0x01020304", + "message": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004", + "hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310", + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] +} + +``` + +### ShowInfo + +The UI should show the info to the user. Does not expect response. + +#### Sample call + +```json +{ + "jsonrpc": "2.0", + "id": 9, + "method": "ShowInfo", + "params": [ + { + "text": "Tests completed" + } + ] +} + +``` + +### ShowError + +The UI should show the info to the user. Does not expect response. + +```json + +{ + "jsonrpc": "2.0", + "id": 2, + "method": "ShowError", + "params": [ + { + "text": "Testing 'ShowError'" + } + ] +} + +``` + +### OnApproved + +`OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions. + +When implementing rate-limited rules, this callback should be used. + +TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`. + +### OnSignerStartup + +This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external api, +in k/v-form. + +Example call: +```json + +{ + "jsonrpc": "2.0", + "id": 1, + "method": "OnSignerStartup", + "params": [ + { + "info": { + "extapi_http": "http://localhost:8550", + "extapi_ipc": null, + "extapi_version": "2.0.0", + "intapi_version": "1.2.0" + } + } + ] +} + +``` + + +### Rules for UI apis + +A UI should conform to the following rules. + +* A UI MUST NOT load any external resources that were not embedded/part of the UI package. + * For example, not load icons, stylesheets from the internet + * Not load files from the filesystem, unless they reside in the same local directory (e.g. config files) +* A Graphical UI MUST show the blocky-identicon for ethereum addresses. +* A UI MUST warn display approproate warning if the destination-account is formatted with invalid checksum. +* A UI MUST NOT open any ports or services + * The signer opens the public port +* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write. +* A UI SHOULD inform the user about the `SHA256` or `MD5` hash of the binary being executed +* A UI SHOULD NOT maintain a secondary storage of data, e.g. list of accounts + * The signer provides accounts +* A UI SHOULD, to the best extent possible, use static linking / bundling, so that requried libraries are bundled +along with the UI. + + diff --git a/cmd/clef/extapi_changelog.md b/cmd/clef/extapi_changelog.md new file mode 100644 index 0000000000..2014e90ae4 --- /dev/null +++ b/cmd/clef/extapi_changelog.md @@ -0,0 +1,25 @@ +### Changelog for external API + + + +#### 2.0.0 + +* Commit `73abaf04b1372fa4c43201fb1b8019fe6b0a6f8d`, move `from` into `transaction` object in `signTransaction`. This +makes the `accounts_signTransaction` identical to the old `eth_signTransaction`. + + +#### 1.0.0 + +Initial release. + +### Versioning + +The API uses [semantic versioning](https://semver.org/). + +TLDR; Given a version number MAJOR.MINOR.PATCH, increment the: + +* MAJOR version when you make incompatible API changes, +* MINOR version when you add functionality in a backwards-compatible manner, and +* PATCH version when you make backwards-compatible bug fixes. + +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. diff --git a/cmd/clef/intapi_changelog.md b/cmd/clef/intapi_changelog.md new file mode 100644 index 0000000000..7d2a897ea2 --- /dev/null +++ b/cmd/clef/intapi_changelog.md @@ -0,0 +1,86 @@ +### Changelog for internal API (ui-api) + +### 2.0.0 + +* Modify how `call_info` on a transaction is conveyed. New format: + +``` +{ + "jsonrpc": "2.0", + "id": 2, + "method": "ApproveTx", + "params": [ + { + "transaction": { + "from": "0x82A2A876D39022B3019932D30Cd9c97ad5616813", + "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "gas": "0x333", + "gasPrice": "0x123", + "value": "0x10", + "nonce": "0x0", + "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", + "input": null + }, + "call_info": [ + { + "type": "WARNING", + "message": "Invalid checksum on to-address" + }, + { + "type": "WARNING", + "message": "Tx contains data, but provided ABI signature could not be matched: Did not match: test (0 matches)" + } + ], + "meta": { + "remote": "127.0.0.1:54286", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + } + ] +} +``` + +#### 1.2.0 + +* Add `OnStartup` method, to provide the UI with information about what API version +the signer uses (both internal and external) aswell as build-info and external api. + +Example call: +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "OnSignerStartup", + "params": [ + { + "info": { + "extapi_http": "http://localhost:8550", + "extapi_ipc": null, + "extapi_version": "2.0.0", + "intapi_version": "1.2.0" + } + } + ] +} +``` + +#### 1.1.0 + +* Add `OnApproved` method + +#### 1.0.0 + +Initial release. + +### Versioning + +The API uses [semantic versioning](https://semver.org/). + +TLDR; Given a version number MAJOR.MINOR.PATCH, increment the: + +* MAJOR version when you make incompatible API changes, +* MINOR version when you add functionality in a backwards-compatible manner, and +* PATCH version when you make backwards-compatible bug fixes. + +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. diff --git a/cmd/clef/main.go b/cmd/clef/main.go new file mode 100644 index 0000000000..fb91f4c258 --- /dev/null +++ b/cmd/clef/main.go @@ -0,0 +1,640 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// signer is a utility that can be used so sign transactions and +// arbitrary data. +package main + +import ( + "bufio" + "context" + "crypto/rand" + "crypto/sha256" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + + "encoding/hex" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/signer/core" + "github.com/ethereum/go-ethereum/signer/rules" + "github.com/ethereum/go-ethereum/signer/storage" + "gopkg.in/urfave/cli.v1" + "os/signal" +) + +// ExternalApiVersion -- see extapi_changelog.md +const ExternalApiVersion = "2.0.0" + +// InternalApiVersion -- see intapi_changelog.md +const InternalApiVersion = "2.0.0" + +const legalWarning = ` +WARNING! + +Clef is alpha software, and not yet publically released. This software has _not_ been audited, and there +are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software +unless you agree to take full responsibility for doing so, and know what you are doing. + +TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE! + +` + +var ( + logLevelFlag = cli.IntFlag{ + Name: "loglevel", + Value: 4, + Usage: "log level to emit to the screen", + } + keystoreFlag = cli.StringFlag{ + Name: "keystore", + Value: filepath.Join(node.DefaultDataDir(), "keystore"), + Usage: "Directory for the keystore", + } + configdirFlag = cli.StringFlag{ + Name: "configdir", + Value: DefaultConfigDir(), + Usage: "Directory for Clef configuration", + } + rpcPortFlag = cli.IntFlag{ + Name: "rpcport", + Usage: "HTTP-RPC server listening port", + Value: node.DefaultHTTPPort + 5, + } + signerSecretFlag = cli.StringFlag{ + Name: "signersecret", + Usage: "A file containing the password used to encrypt Clef credentials, e.g. keystore credentials and ruleset hash", + } + dBFlag = cli.StringFlag{ + Name: "4bytedb", + Usage: "File containing 4byte-identifiers", + Value: "./4byte.json", + } + customDBFlag = cli.StringFlag{ + Name: "4bytedb-custom", + Usage: "File used for writing new 4byte-identifiers submitted via API", + Value: "./4byte-custom.json", + } + auditLogFlag = cli.StringFlag{ + Name: "auditlog", + Usage: "File used to emit audit logs. Set to \"\" to disable", + Value: "audit.log", + } + ruleFlag = cli.StringFlag{ + Name: "rules", + Usage: "Enable rule-engine", + Value: "rules.json", + } + stdiouiFlag = cli.BoolFlag{ + Name: "stdio-ui", + Usage: "Use STDIN/STDOUT as a channel for an external UI. " + + "This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user " + + "interface, and can be used when Clef is started by an external process.", + } + testFlag = cli.BoolFlag{ + Name: "stdio-ui-test", + Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.", + } + app = cli.NewApp() + initCommand = cli.Command{ + Action: utils.MigrateFlags(initializeSecrets), + Name: "init", + Usage: "Initialize the signer, generate secret storage", + ArgsUsage: "", + Flags: []cli.Flag{ + logLevelFlag, + configdirFlag, + }, + Description: ` +The init command generates a master seed which Clef can use to store credentials and data needed for +the rule-engine to work.`, + } + attestCommand = cli.Command{ + Action: utils.MigrateFlags(attestFile), + Name: "attest", + Usage: "Attest that a js-file is to be used", + ArgsUsage: "", + Flags: []cli.Flag{ + logLevelFlag, + configdirFlag, + signerSecretFlag, + }, + Description: ` +The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of +incoming requests. + +Whenever you make an edit to the rule file, you need to use attestation to tell +Clef that the file is 'safe' to execute.`, + } + + addCredentialCommand = cli.Command{ + Action: utils.MigrateFlags(addCredential), + Name: "addpw", + Usage: "Store a credential for a keystore file", + ArgsUsage: "
", + Flags: []cli.Flag{ + logLevelFlag, + configdirFlag, + signerSecretFlag, + }, + Description: ` +The addpw command stores a password for a given address (keyfile). If you invoke it with only one parameter, it will +remove any stored credential for that address (keyfile) +`, + } +) + +func init() { + app.Name = "Clef" + app.Usage = "Manage Ethereum account operations" + app.Flags = []cli.Flag{ + logLevelFlag, + keystoreFlag, + configdirFlag, + utils.NetworkIdFlag, + utils.LightKDFFlag, + utils.NoUSBFlag, + utils.RPCListenAddrFlag, + utils.RPCVirtualHostsFlag, + utils.IPCDisabledFlag, + utils.IPCPathFlag, + utils.RPCEnabledFlag, + rpcPortFlag, + signerSecretFlag, + dBFlag, + customDBFlag, + auditLogFlag, + ruleFlag, + stdiouiFlag, + testFlag, + } + app.Action = signer + app.Commands = []cli.Command{initCommand, attestCommand, addCredentialCommand} + +} +func main() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func initializeSecrets(c *cli.Context) error { + if err := initialize(c); err != nil { + return err + } + configDir := c.String(configdirFlag.Name) + + masterSeed := make([]byte, 256) + n, err := io.ReadFull(rand.Reader, masterSeed) + if err != nil { + return err + } + if n != len(masterSeed) { + return fmt.Errorf("failed to read enough random") + } + err = os.Mkdir(configDir, 0700) + if err != nil && !os.IsExist(err) { + return err + } + location := filepath.Join(configDir, "secrets.dat") + if _, err := os.Stat(location); err == nil { + return fmt.Errorf("file %v already exists, will not overwrite", location) + } + err = ioutil.WriteFile(location, masterSeed, 0700) + if err != nil { + return err + } + fmt.Printf("A master seed has been generated into %s\n", location) + fmt.Printf(` +This is required to be able to store credentials, such as : +* Passwords for keystores (used by rule engine) +* Storage for javascript rules +* Hash of rule-file + +You should treat that file with utmost secrecy, and make a backup of it. +NOTE: This file does not contain your accounts. Those need to be backed up separately! + +`) + return nil +} +func attestFile(ctx *cli.Context) error { + if len(ctx.Args()) < 1 { + utils.Fatalf("This command requires an argument.") + } + if err := initialize(ctx); err != nil { + return err + } + + stretchedKey, err := readMasterKey(ctx) + if err != nil { + utils.Fatalf(err.Error()) + } + configDir := ctx.String(configdirFlag.Name) + vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) + confKey := crypto.Keccak256([]byte("config"), stretchedKey) + + // Initialize the encrypted storages + configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confKey) + val := ctx.Args().First() + configStorage.Put("ruleset_sha256", val) + log.Info("Ruleset attestation updated", "sha256", val) + return nil +} + +func addCredential(ctx *cli.Context) error { + if len(ctx.Args()) < 1 { + utils.Fatalf("This command requires at leaste one argument.") + } + if err := initialize(ctx); err != nil { + return err + } + + stretchedKey, err := readMasterKey(ctx) + if err != nil { + utils.Fatalf(err.Error()) + } + configDir := ctx.String(configdirFlag.Name) + vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) + pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) + + // Initialize the encrypted storages + pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) + key := ctx.Args().First() + value := "" + if len(ctx.Args()) > 1 { + value = ctx.Args().Get(1) + } + pwStorage.Put(key, value) + log.Info("Credential store updated", "key", key) + return nil +} + +func initialize(c *cli.Context) error { + // Set up the logger to print everything + logOutput := os.Stdout + if c.Bool(stdiouiFlag.Name) { + logOutput = os.Stderr + // If using the stdioui, we can't do the 'confirm'-flow + fmt.Fprintf(logOutput, legalWarning) + } else { + if !confirm(legalWarning) { + return fmt.Errorf("aborted by user") + } + } + + log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(logOutput, log.TerminalFormat(true)))) + return nil +} + +func signer(c *cli.Context) error { + if err := initialize(c); err != nil { + return err + } + var ( + ui core.SignerUI + ) + if c.Bool(stdiouiFlag.Name) { + log.Info("Using stdin/stdout as UI-channel") + ui = core.NewStdIOUI() + } else { + log.Info("Using CLI as UI-channel") + ui = core.NewCommandlineUI() + } + db, err := core.NewAbiDBFromFiles(c.String(dBFlag.Name), c.String(customDBFlag.Name)) + if err != nil { + utils.Fatalf(err.Error()) + } + log.Info("Loaded 4byte db", "signatures", db.Size(), "file", c.String("4bytedb")) + + var ( + api core.ExternalAPI + ) + + configDir := c.String(configdirFlag.Name) + if stretchedKey, err := readMasterKey(c); err != nil { + log.Info("No master seed provided, rules disabled") + } else { + + if err != nil { + utils.Fatalf(err.Error()) + } + vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) + + // Generate domain specific keys + pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) + jskey := crypto.Keccak256([]byte("jsstorage"), stretchedKey) + confkey := crypto.Keccak256([]byte("config"), stretchedKey) + + // Initialize the encrypted storages + pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) + jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey) + configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey) + + //Do we have a rule-file? + ruleJS, err := ioutil.ReadFile(c.String(ruleFlag.Name)) + if err != nil { + log.Info("Could not load rulefile, rules not enabled", "file", "rulefile") + } else { + hasher := sha256.New() + hasher.Write(ruleJS) + shasum := hasher.Sum(nil) + storedShasum := configStorage.Get("ruleset_sha256") + if storedShasum != hex.EncodeToString(shasum) { + log.Info("Could not validate ruleset hash, rules not enabled", "got", hex.EncodeToString(shasum), "expected", storedShasum) + } else { + // Initialize rules + ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage, pwStorage) + if err != nil { + utils.Fatalf(err.Error()) + } + ruleEngine.Init(string(ruleJS)) + ui = ruleEngine + log.Info("Rule engine configured", "file", c.String(ruleFlag.Name)) + } + } + } + + apiImpl := core.NewSignerAPI( + c.Int64(utils.NetworkIdFlag.Name), + c.String(keystoreFlag.Name), + c.Bool(utils.NoUSBFlag.Name), + ui, db, + c.Bool(utils.LightKDFFlag.Name)) + + api = apiImpl + + // Audit logging + if logfile := c.String(auditLogFlag.Name); logfile != "" { + api, err = core.NewAuditLogger(logfile, api) + if err != nil { + utils.Fatalf(err.Error()) + } + log.Info("Audit logs configured", "file", logfile) + } + // register signer API with server + var ( + extapiUrl = "n/a" + ipcApiUrl = "n/a" + ) + rpcApi := []rpc.API{ + { + Namespace: "account", + Public: true, + Service: api, + Version: "1.0"}, + } + if c.Bool(utils.RPCEnabledFlag.Name) { + + vhosts := splitAndTrim(c.GlobalString(utils.RPCVirtualHostsFlag.Name)) + cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name)) + + // start http server + httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name)) + listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcApi, []string{"account"}, cors, vhosts) + if err != nil { + utils.Fatalf("Could not start RPC api: %v", err) + } + extapiUrl = fmt.Sprintf("http://%s", httpEndpoint) + log.Info("HTTP endpoint opened", "url", extapiUrl) + + defer func() { + listener.Close() + log.Info("HTTP endpoint closed", "url", httpEndpoint) + }() + + } + if !c.Bool(utils.IPCDisabledFlag.Name) { + if c.IsSet(utils.IPCPathFlag.Name) { + ipcApiUrl = c.String(utils.IPCPathFlag.Name) + } else { + ipcApiUrl = filepath.Join(configDir, "clef.ipc") + } + + listener, _, err := rpc.StartIPCEndpoint(func() bool { return true }, ipcApiUrl, rpcApi) + if err != nil { + utils.Fatalf("Could not start IPC api: %v", err) + } + log.Info("IPC endpoint opened", "url", ipcApiUrl) + defer func() { + listener.Close() + log.Info("IPC endpoint closed", "url", ipcApiUrl) + }() + + } + + if c.Bool(testFlag.Name) { + log.Info("Performing UI test") + go testExternalUI(apiImpl) + } + ui.OnSignerStartup(core.StartupInfo{ + Info: map[string]interface{}{ + "extapi_version": ExternalApiVersion, + "intapi_version": InternalApiVersion, + "extapi_http": extapiUrl, + "extapi_ipc": ipcApiUrl, + }, + }) + + abortChan := make(chan os.Signal) + signal.Notify(abortChan, os.Interrupt) + + sig := <-abortChan + log.Info("Exiting...", "signal", sig) + + return nil +} + +// splitAndTrim splits input separated by a comma +// and trims excessive white space from the substrings. +func splitAndTrim(input string) []string { + result := strings.Split(input, ",") + for i, r := range result { + result[i] = strings.TrimSpace(r) + } + return result +} + +// DefaultConfigDir is the default config directory to use for the vaults and other +// persistence requirements. +func DefaultConfigDir() string { + // Try to place the data folder in the user's home dir + home := homeDir() + if home != "" { + if runtime.GOOS == "darwin" { + return filepath.Join(home, "Library", "Signer") + } else if runtime.GOOS == "windows" { + return filepath.Join(home, "AppData", "Roaming", "Signer") + } else { + return filepath.Join(home, ".clef") + } + } + // As we cannot guess a stable location, return empty and handle later + return "" +} + +func homeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" +} +func readMasterKey(ctx *cli.Context) ([]byte, error) { + var ( + file string + configDir = ctx.String(configdirFlag.Name) + ) + if ctx.IsSet(signerSecretFlag.Name) { + file = ctx.String(signerSecretFlag.Name) + } else { + file = filepath.Join(configDir, "secrets.dat") + } + if err := checkFile(file); err != nil { + return nil, err + } + masterKey, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + if len(masterKey) < 256 { + return nil, fmt.Errorf("master key of insufficient length, expected >255 bytes, got %d", len(masterKey)) + } + // Create vault location + vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterKey)[:10])) + err = os.Mkdir(vaultLocation, 0700) + if err != nil && !os.IsExist(err) { + return nil, err + } + //!TODO, use KDF to stretch the master key + // stretched_key := stretch_key(master_key) + + return masterKey, nil +} + +// checkFile is a convenience function to check if a file +// * exists +// * is mode 0600 +func checkFile(filename string) error { + info, err := os.Stat(filename) + if err != nil { + return fmt.Errorf("failed stat on %s: %v", filename, err) + } + // Check the unix permission bits + if info.Mode().Perm()&077 != 0 { + return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String()) + } + return nil +} + +// confirm displays a text and asks for user confirmation +func confirm(text string) bool { + fmt.Printf(text) + fmt.Printf("\nEnter 'ok' to proceed:\n>") + + text, err := bufio.NewReader(os.Stdin).ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + + if text := strings.TrimSpace(text); text == "ok" { + return true + } + return false +} + +func testExternalUI(api *core.SignerAPI) { + + ctx := context.WithValue(context.Background(), "remote", "clef binary") + ctx = context.WithValue(ctx, "scheme", "in-proc") + ctx = context.WithValue(ctx, "local", "main") + + errs := make([]string, 0) + + api.UI.ShowInfo("Testing 'ShowInfo'") + api.UI.ShowError("Testing 'ShowError'") + + checkErr := func(method string, err error) { + if err != nil && err != core.ErrRequestDenied { + errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error())) + } + } + var err error + + _, err = api.SignTransaction(ctx, core.SendTxArgs{From: common.MixedcaseAddress{}}, nil) + checkErr("SignTransaction", err) + _, err = api.Sign(ctx, common.MixedcaseAddress{}, common.Hex2Bytes("01020304")) + checkErr("Sign", err) + _, err = api.List(ctx) + checkErr("List", err) + _, err = api.New(ctx) + checkErr("New", err) + _, err = api.Export(ctx, common.Address{}) + checkErr("Export", err) + _, err = api.Import(ctx, json.RawMessage{}) + checkErr("Import", err) + + api.UI.ShowInfo("Tests completed") + + if len(errs) > 0 { + log.Error("Got errors") + for _, e := range errs { + log.Error(e) + } + } else { + log.Info("No errors") + } + +} + +/** +//Create Account + +curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_new","params":["test"],"id":67}' localhost:8550 + +// List accounts + +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_list","params":[""],"id":67}' http://localhost:8550/ + +// Make Transaction +// safeSend(0x12) +// 4401a6e40000000000000000000000000000000000000000000000000000000000000012 + +// supplied abi +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x82A2A876D39022B3019932D30Cd9c97ad5616813","gas":"0x333","gasPrice":"0x123","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x10", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"test"],"id":67}' http://localhost:8550/ + +// Not supplied +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x82A2A876D39022B3019932D30Cd9c97ad5616813","gas":"0x333","gasPrice":"0x123","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x10", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"}],"id":67}' http://localhost:8550/ + +// Sign data + +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_sign","params":["0x694267f14675d7e1b9494fd8d72fefe1755710fa","bazonk gaz baz"],"id":67}' http://localhost:8550/ + + +**/ diff --git a/cmd/clef/pythonsigner.py b/cmd/clef/pythonsigner.py new file mode 100644 index 0000000000..46fa23bd8c --- /dev/null +++ b/cmd/clef/pythonsigner.py @@ -0,0 +1,179 @@ +import os,sys, subprocess +from tinyrpc.transports import ServerTransport +from tinyrpc.protocols.jsonrpc import JSONRPCProtocol +from tinyrpc.dispatch import public,RPCDispatcher +from tinyrpc.server import RPCServer + +""" This is a POC example of how to write a custom UI for Clef. The UI starts the +clef process with the '--stdio-ui' option, and communicates with clef using standard input / output. + +The standard input/output is a relatively secure way to communicate, as it does not require opening any ports +or IPC files. Needless to say, it does not protect against memory inspection mechanisms where an attacker +can access process memory.""" + +try: + import urllib.parse as urlparse +except ImportError: + import urllib as urlparse + +class StdIOTransport(ServerTransport): + """ Uses std input/output for RPC """ + def receive_message(self): + return None, urlparse.unquote(sys.stdin.readline()) + + def send_reply(self, context, reply): + print(reply) + +class PipeTransport(ServerTransport): + """ Uses std a pipe for RPC """ + + def __init__(self,input, output): + self.input = input + self.output = output + + def receive_message(self): + data = self.input.readline() + print(">> {}".format( data)) + return None, urlparse.unquote(data) + + def send_reply(self, context, reply): + print("<< {}".format( reply)) + self.output.write(reply) + self.output.write("\n") + +class StdIOHandler(): + + def __init__(self): + pass + + @public + def ApproveTx(self,req): + """ + Example request: + { + "jsonrpc": "2.0", + "method": "ApproveTx", + "params": [{ + "transaction": { + "to": "0xae967917c465db8578ca9024c205720b1a3651A9", + "gas": "0x333", + "gasPrice": "0x123", + "value": "0x10", + "data": "0xd7a5865800000000000000000000000000000000000000000000000000000000000000ff", + "nonce": "0x0" + }, + "from": "0xAe967917c465db8578ca9024c205720b1a3651A9", + "call_info": "Warning! Could not validate ABI-data against calldata\nSupplied ABI spec does not contain method signature in data: 0xd7a58658", + "meta": { + "remote": "127.0.0.1:34572", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + }], + "id": 1 + } + + :param transaction: transaction info + :param call_info: info abou the call, e.g. if ABI info could not be + :param meta: metadata about the request, e.g. where the call comes from + :return: + """ + transaction = req.get('transaction') + _from = req.get('from') + call_info = req.get('call_info') + meta = req.get('meta') + + return { + "approved" : False, + #"transaction" : transaction, + # "from" : _from, +# "password" : None, + } + + @public + def ApproveSignData(self, req): + """ Example request + + """ + return {"approved": False, "password" : None} + + @public + def ApproveExport(self, req): + """ Example request + + """ + return {"approved" : False} + + @public + def ApproveImport(self, req): + """ Example request + + """ + return { "approved" : False, "old_password": "", "new_password": ""} + + @public + def ApproveListing(self, req): + """ Example request + + """ + return {'accounts': []} + + @public + def ApproveNewAccount(self, req): + """ + Example request + + :return: + """ + return {"approved": False, + #"password": "" + } + + @public + def ShowError(self,message = {}): + """ + Example request: + + {"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowError'"},"id":1} + + :param message: to show + :return: nothing + """ + if 'text' in message.keys(): + sys.stderr.write("Error: {}\n".format( message['text'])) + return + + @public + def ShowInfo(self,message = {}): + """ + Example request + {"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowInfo'"},"id":0} + + :param message: to display + :return:nothing + """ + + if 'text' in message.keys(): + sys.stdout.write("Error: {}\n".format( message['text'])) + return + +def main(args): + + cmd = ["./clef", "--stdio-ui"] + if len(args) > 0 and args[0] == "test": + cmd.extend(["--stdio-ui-test"]) + print("cmd: {}".format(" ".join(cmd))) + dispatcher = RPCDispatcher() + dispatcher.register_instance(StdIOHandler(), '') + # line buffered + p = subprocess.Popen(cmd, bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + rpc_server = RPCServer( + PipeTransport(p.stdout, p.stdin), + JSONRPCProtocol(), + dispatcher + ) + rpc_server.serve_forever() + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/cmd/clef/rules.md b/cmd/clef/rules.md new file mode 100644 index 0000000000..327ba765c5 --- /dev/null +++ b/cmd/clef/rules.md @@ -0,0 +1,236 @@ +# Rules + +The `signer` binary contains a ruleset engine, implemented with [OttoVM](https://github.com/robertkrimen/otto) + +It enables usecases like the following: + +* I want to auto-approve transactions with contract `CasinoDapp`, with up to `0.05 ether` in value to maximum `1 ether` per 24h period +* I want to auto-approve transaction to contract `EthAlarmClock` with `data`=`0xdeadbeef`, if `value=0`, `gas < 44k` and `gasPrice < 40Gwei` + +The two main features that are required for this to work well are; + +1. Rule Implementation: how to create, manage and interpret rules in a flexible but secure manner +2. Credential managements and credentials; how to provide auto-unlock without exposing keys unnecessarily. + +The section below deals with both of them + +## Rule Implementation + +A ruleset file is implemented as a `js` file. Under the hood, the ruleset-engine is a `SignerUI`, implementing the same methods as the `json-rpc` methods +defined in the UI protocol. Example: + +```javascript + +function asBig(str){ + if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)} + return new BigNumber(str) +} + +// Approve transactions to a certain contract if value is below a certain limit +function ApproveTx(req){ + + var limit = big.Newint("0xb1a2bc2ec50000") + var value = asBig(req.transaction.value); + + if(req.transaction.to.toLowerCase()=="0xae967917c465db8578ca9024c205720b1a3651a9") + && value.lt(limit) ){ + return "Approve" + } + // If we return "Reject", it will be rejected. + // By not returning anything, it will be passed to the next UI, for manual processing +} + +//Approve listings if request made from IPC +function ApproveListing(req){ + if (req.metadata.scheme == "ipc"){ return "Approve"} +} + +``` + +Whenever the external API is called (and the ruleset is enabled), the `signer` calls the UI, which is an instance of a ruleset-engine. The ruleset-engine +invokes the corresponding method. In doing so, there are three possible outcomes: + +1. JS returns "Approve" + * Auto-approve request +2. JS returns "Reject" + * Auto-reject request +3. Error occurs, or something else is returned + * Pass on to `next` ui: the regular UI channel. + +A more advanced example can be found below, "Example 1: ruleset for a rate-limited window", using `storage` to `Put` and `Get` `string`s by key. + +* At the time of writing, storage only exists as an ephemeral unencrypted implementation, to be used during testing. + +### Things to note + +The Otto vm has a few [caveats](https://github.com/robertkrimen/otto): + +* "use strict" will parse, but does nothing. +* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. +* Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. + +Additionally, a few more have been added + +* The rule execution cannot load external javascript files. +* The only preloaded libary is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository. +* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data. +* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes. +* The JS engine has access to `storage` and `console`. + +#### Security considerations + +##### Security of ruleset + +Some security precautions can be made, such as: + +* Never load `ruleset.js` unless the file is `readonly` (`r-??-??-?`). If the user wishes to modify the ruleset, he must make it writeable and then set back to readonly. + * This is to prevent attacks where files are dropped on the users disk. +* Since we're going to have to have some form of secure storage (not defined in this section), we could also store the `sha3` of the `ruleset.js` file in there. + * If the user wishes to modify the ruleset, he'd then have to perform e.g. `signer --attest /path/to/ruleset --credential ` + +##### Security of implementation + +The drawbacks of this very flexible solution is that the `signer` needs to contain a javascript engine. This is pretty simple to implement, since it's already +implemented for `geth`. There are no known security vulnerabilities in, nor have we had any security-problems with it so far. + +The javascript engine would be an added attack surface; but if the validation of `rulesets` is made good (with hash-based attestation), the actual javascript cannot be considered +an attack surface -- if an attacker can control the ruleset, a much simpler attack would be to implement an "always-approve" rule instead of exploiting the js vm. The only benefit +to be gained from attacking the actual `signer` process from the `js` side would be if it could somehow extract cryptographic keys from memory. + +##### Security in usability + +Javascript is flexible, but also easy to get wrong, especially when users assume that `js` can handle large integers natively. Typical errors +include trying to multiply `gasCost` with `gas` without using `bigint`:s. + +It's unclear whether any other DSL could be more secure; since there's always the possibility of erroneously implementing a rule. + + +## Credential management + +The ability to auto-approve transaction means that the signer needs to have necessary credentials to decrypt keyfiles. These passwords are hereafter called `ksp` (keystore pass). + +### Example implementation + +Upon startup of the signer, the signer is given a switch: `--seed ` +The `seed` contains a blob of bytes, which is the master seed for the `signer`. + +The `signer` uses the `seed` to: + +* Generate the `path` where the settings are stored. + * `./settings/1df094eb-c2b1-4689-90dd-790046d38025/vault.dat` + * `./settings/1df094eb-c2b1-4689-90dd-790046d38025/rules.js` +* Generate the encryption password for `vault.dat`. + +The `vault.dat` would be an encrypted container storing the following information: + +* `ksp` entries +* `sha256` hash of `rules.js` +* Information about pair:ed callers (not yet specified) + +### Security considerations + +This would leave it up to the user to ensure that the `path/to/masterseed` is handled in a secure way. It's difficult to get around this, although one could +imagine leveraging OS-level keychains where supported. The setup is however in general similar to how ssh-keys are stored in `.ssh/`. + + +# Implementation status + +This is now implemented (with ephemeral non-encrypted storage for now, so not yet enabled). + +## Example 1: ruleset for a rate-limited window + + +```javascript + + function big(str){ + if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)} + return new BigNumber(str) + } + + // Time window: 1 week + var window = 1000* 3600*24*7; + + // Limit : 1 ether + var limit = new BigNumber("1e18"); + + function isLimitOk(transaction){ + var value = big(transaction.value) + // Start of our window function + var windowstart = new Date().getTime() - window; + + var txs = []; + var stored = storage.Get('txs'); + + if(stored != ""){ + txs = JSON.parse(stored) + } + // First, remove all that have passed out of the time-window + var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart}); + console.log(txs, newtxs.length); + + // Secondly, aggregate the current sum + sum = new BigNumber(0) + + sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum); + console.log("ApproveTx > Sum so far", sum); + console.log("ApproveTx > Requested", value.toNumber()); + + // Would we exceed weekly limit ? + return sum.plus(value).lt(limit) + + } + function ApproveTx(r){ + if (isLimitOk(r.transaction)){ + return "Approve" + } + return "Nope" + } + + /** + * OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter + * 'response_str' contains the return value that will be sent to the external caller. + * The return value from this method is ignore - the reason for having this callback is to allow the + * ruleset to keep track of approved transactions. + * + * When implementing rate-limited rules, this callback should be used. + * If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user + * then accepts the transaction, this method will be called. + * + * TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx. + */ + function OnApprovedTx(resp){ + var value = big(resp.tx.value) + var txs = [] + // Load stored transactions + var stored = storage.Get('txs'); + if(stored != ""){ + txs = JSON.parse(stored) + } + // Add this to the storage + txs.push({tstamp: new Date().getTime(), value: value}); + storage.Put("txs", JSON.stringify(txs)); + } + +``` + +## Example 2: allow destination + +```javascript + + function ApproveTx(r){ + if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"} + if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"} + // Otherwise goes to manual processing + } + +``` + +## Example 3: Allow listing + +```javascript + + function ApproveListing(){ + return "Approve" + } + +``` \ No newline at end of file diff --git a/cmd/clef/sign_flow.png b/cmd/clef/sign_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..9c0f3cc5d58b9bd142a920525fd339e2bd0776a5 GIT binary patch literal 36397 zcmd431z1)4wl};81w>E@gLIPuN=P@TNQX2?sB}noix^1Bq8pX&?h+LQB_yRwK)SoW zG4Y&x?tSmM=eghee((2OpS|5|)>?DT`HwNiFUH?rNkNj}9N9S(3Pm6-bx#?E!tO+& zFk^Ag!f#IgO6i6FVB6o3R>g%s9=L|DQK;)E>3ia;F7b;a?k=jmwYw+Is3Yz^F`~D! z6{J}{>V>!x@0hX0!_VQ;kaoT)&&vJMr@q9$ec)osRMIyX@MwYZLB@NE0-2I7+BdV0 z#97Y0$u+%qFH}~7Z2CR==)j=EFUGKllNc>`eWib6kq|B=7Q(Zskkh)9#eBu2RUFtMX(TVf1n5b8hZN6+4n}p#B=U)j1Zk~=l zbkYaeT4h*&djPWHKq6D|ixiv9FA2!!v-8z1%zTdt+cHV33z?jpv~(`+F>H(H=Uhm_ zz!_W{8aw70DwkL0u5wz6-RgL)+FWART{OatrtAHB$@I^7C}R`<-~Y+aO*)yo>1zzj+xg?{!-*x19-1pF$ zo{^DpdJQhf>+lmEmRvG9Q@U~?ueylRH|2S!?-_i)qs?x!fg*a|5eH&UvetJnDyRU~%c`gxPiF2Jh+#s0$ zW&QD1e$CFPyTjs7j-d%Df!BvKy!8_c`X>Sbmv6yS;`juxR^71M{8GC!I;1oqtz65$ z{*@N__t~m_zHAh0y!&#cR&A=*6+Sta_2EmXTw^0*+_Pqaj|pw_)N^qlZP7}CL1$tYV#wWJ0qL(y#c70lemVT_71#|JCvH8n!h3v6|}GzQJVsYc%-zsGRCGV4xT zTP*IlZQhfSvU5|b$WNB7Hay>R$>MNM(fhrAz1LyC_mM`#w)w`QjKJ#V@<<$Gk_Y-= zr9Mfw+DW4C)~#EqB_-j{uyE8Ln!u9Karhyzxil12X+L}WQ3EPXAtAPCIZ@cXVs9oc zhSP+QMLj23tJqk_W9^HyDthmCzbQ&j@)AJ# zMzCnyFLPe5oINWcA@LC&nz@z7YWQl2c`tLcu)CA;NN=_Vj#$J-x&^F0@kKXYpgwa4?Djr6xC2;xNP*hv?_F1ao$DymuIVtWx={B2SawT^uoH|Z9Gd@MRU-a$JbCi#^xyv(R9H-k9>|V2RnM*OzFsM&us# zW~s%yO!`X)j3(MPT|?#QR2IEoEbZ4b=u8%u?k;6sQ9V=Y?nF=N0@rAIL&~CAWQYeV z%=ln?$z!K_%n07*u|Jo7t-RcA%lu71K$wWZ%k$UJMa%DAQME$2(O>g?@Zi$L<;W|u z9S>mDo1OH?V&=lb_a+q#)Tvx1B`vg>xMOE;U%B4OGB;StQtQ5Byth70>bSi;@@=Lq zPOIP%20Wm2^I)lk>q_m8a$>qlx`Ly#xIl)E+gvKLhsu|1jEqPwUuKAyo}Lbk zifWdm_QV!*b>)9pX8BG&jz@Ycm~ZlR%8JDStZa5}>(TgkcVF3kyH&p0CCg?zo8Y`& zUVn7UjJGKmBEzUc_jxJg36MRr2|UvJ;6Oo^IIEkx|ut13TDIXI}Hla`mLU2NQY z!RGO?soq zi)_{v#ey%r+XE2}HWiy)vX#qK3%kvt$DuzC;GT!dtreN`4+p|oy@O@4g5|ZDj*ZNBMo8 z{uz(!x*?UXJn3C^Pv z+e6l~Jt~Uw`)9o8dos^c2}MtQdS$=%2kA8T>S4F)%Jat|YK0)SU zR`i5l&}mVMISD2Q($N!`5go@~HOtYOm`s)Qu6%t_2>IF%R$?vk9)Es)>3b}9z($8? zQmSlJ(W8&P%(H6)5wS02Su6eS67u^!_;CLR>@FV5pAFO}yOM)tPrrTkCuW(4sm^_} zIS+yGXQ{;%>-r-eRIYYuR&?6lC+g$3f8Bj)^YP3Tn~4`!)*U7|)brRWK%>!t&Zu*k&J#jDJ?2;1?-4V|;rgWL@;$$fn+Q>2JS9xw z-1^4GbEl=jGsKJv=XstEs;0J8uha?t{_#PwGfOS|^evDGw2fZXT$ef-$PKQxk)_C!^qbnQr zCr2yUM>;I9V-|##8=0g9q7CE41eIdM1RoiOiwVAs5eqA#`GBo`7h?SVPKX1RuO846 zn<{@$&4yocJY$d+xY+ZJL;*sqdy>aGp6FhSB4n$pFmU6&8u~_1U+TC{M+^|kL&0c{ zmP?jK_k88D=nI6Dvj$6l72;lZrV3--PVLIt2kN66L+_AMTON(aC592Kkr3d-76v?_~f7A5ZDPwUsfkwTZ$Q^50|7Q&Rt+C;~SJ@v(uuSx7NCph<%%dkRwQn+akZ!&i{ zT}Q{}T=Gl0OFuw(51sOI5*MBgffG!=$W#80{;6c&{_;hXWK&%d<$=ij)xq%xJgf%K ztkbo2u048i^n&K}_DRe`mr${9V;ZLL#yYT$FGNQul~sw;ntL>1*7^u`WgPIVuIv*? zo<3;{EE_itr_aKu8Z04nf(@a|6(d2^>1BGjC<0aZ6fq((s4=DFy_R8 z^CjdbD9+`eoG?yknl`0F+->u)^x*B#Qga)%fY4mvFx@R-vlbCeCEWc9@8w5Ji>dde6G=N zY_@@*!Dd{r!Pd`HNlTzV)at~@Zd4zuo>8aR^1Kaq0q^CzIvtvE0l36kBSgWi-CR@C zEXwgLd2FlLkL?^NR%yo_lKrweJOqyZOhC_#QaTEw@D>4$=LRct8h2}7J$owl_>R`4 z0-Rm-I|CsGOIz|c1liQp!Pb8D7|fSbowC!+u4!g}VxhH)PB8Cly}T zZY#%f$S8Z7-rv zql|LC2}+q(V(XK~E6sIIQa28mAHQdjy5W91tbtaDVojTK{M;k@f{4|@DZFy0i1{>o z%DHX*s@Yq?z4F}e7dFfX^YHbt5^3Y>uI)tq>_3KgbhDitU<7K;(f8#PmC{oJS4&%&S$$4YwbxafR`hC=jB3$4r=yZ32j+xKgnMA%>1B$QMv zAGp@V?z4q$G=*tW;--J+38xfWkTG)glP(M)V<$Y_8V(ove%?=}AH)}ujs86+6@Br| z0d=Eeejmn3hC_`;fPL+~-ppr=eeVV2h=$aLjY9jnk9B4dxpqt)R-9j}HYK z+fY;iN0pT9Seg>iLJ_B%PT)q5@s;)u&(1Vd`w5!Y;U9)(b?;r*Sy`;dOTV^EBiD2I z>{sS%Q3f6ITR&O~D-^O+y)G${39iUA(&_3hHoD%wmk`-d>xb8HyV0-bH_3|ZjzHH^ zy+a57M0?5@y-tQVZ^5-)VzvHQEIyKo=QeF6>zeNVDvwv*M5GFMJ;=E>48KrOE~~^(r;`qndP-!f?0NecLna6NGX|z1B)P@LKX)bgTTnNv zhwOR!BxsuG|K?96U!729UulOK9-b~`X>*(4fBPf_E^TM4PvijM9ZUt(apKbT(`ze# zvcr!3^DQJF|A+tdP6DW?*aBjodJ{ohX#$3ZlM3W938ju{*iP3uorDc`Y`D?U{?^os zD|%m@uA=9q0hqMK@piz^@Ye0;w4Hw*HU*dAf8(c=`{r%2+`oSPN)ZCuCv08#?#uK3 z9=8gE=2IRj;*{$j&1*<^h&KmQ@^AI1>}@2S)Qmlq6i&x^a|Zvi&B0Q+iQswUMhQ}o zCm~rvpy=IKg!0S_fEQb<0Qn>I0h4~5pfhW=^U6%3`|@KzkiCg)BW~to076)i$~W@X z!-}Krp=(g01B+tN=!aKo#W~L}taMYS;w=(r)HVuwdXh&(%?oZY(HPxT<8x$@MTu+HBMu|OpVnb;qm9@3CvlrSvFAfyX0SE`C={z}4AkZD5KtTaoW}dIv>U*dI zU>`unb0CuhoR_~N3m{ECE~`Xi=s;alQ!0c?*kxzLIjnW6JLn3l&Q~ZeGRsd+_KAsq;@sr)J=}EJkMjEA~duV@;wdm{Jzz z8DVw+WK9j2BpLN&JP419G8v|7;O^J?T&9 zvvTt*8DOlZ-v_Y1w;rqeo*)>7fpwPMalVInYt-xDsdkC!M?leE5ngXP@}Xw6)2$DZ zAqrXLqB~DlMr#qW5eT#tkE14X>y(_FKpbk1x9N#)Zzd<7_+HO&5gwW4BHy7M*;dch zmM}A8GH42X2ka(kzbee2k+%7vKpjw)%>kH?*g6HLP^Zg_>=xch;o6EN4$YT%v0b@3 z)u^Sdg?=_EqpkT~@kI}{U|^}zJQQthZEawV(mif1M?SQ|^Z6<;*W(U!2(3X&&*oe= zFiU{)m|KCyxPtl!%pObYf5AR+TIH9NaD8oSBgm`W9%Sb5L}uPwWM|m^`TO_p9{~YC zYDCU!bzU2Q{CAim(LA9+U~o<`+zmFi{2PKRH5-5UDw?qap!m*;t+uk{tA?k#k6BgC zr2*^Yxz(GTlXKH$A-CN38*BAVlrOn;4M(Qcp%x`DaoYVPTk*yVUWS9CDbiuS$43Vm zLwb{rizC&%$IiTo6C-2O#Ib7TifFPy+{TV~Xc!r3<-4}08~Nm5Y<4a^NkPDEYd&JB z;P-!|q}0;-dz=0P8zk1n^%=II?W8{=IxgDIuZhgA+t)lN&3zWA&e zM2Z2I57sFV0{D05JJ@wq?{k35L{RT^NoQ5j-v}qCdz7~gr6Syb_c@%n;Qgq<2g6~$ z#~(J_{UEY4Z@Fr@yAPZe3!Blvtwhxy8X-JGUgZp#RmH^1a7T!W594Iz(76W>`j;eh zag@C8UPe|xFFpW5+Wrrc7Z6e_82B2?R}#Qv;a_2;NF$*)1f4(qCRwnkv+0?&ni zV~XOq@kQSg_5WaIVaYD~D6XeR1$?4fU$RVk?FG@pi$1{CYmoN?G#{vO<;1^yGdVf= z`W&*Rf!xwdi;9f|^j~HG+jBHXwVwYB$p@@!#nC?;Qc8_T~AL>!j2{nHd0a1ZNS@{R%{}Zrm*9Y z=*qN!5J$ku-getG^;&&Sof4QSf?!|xRgvexcFKBd|Ap1gyB9dT4tG<2Jnp@fk<{Y3 z(4VjWg*BerY_!Hzd+WX7lvhvlk$g??=Lb(uZ%z&hg4pO?(V6n6L+XrM*X6_zLI;nS z0qAXN)q!8htXp&4uj-GMtQUtXi{Iy@(CPT5LV&zh&g-)J2w@Lb>JETD<5OM=S5cUl zj?zz)4532OhO}f>s0Ow7AqUVu3Nde6J_9!I;Or0zQ!N8ggL%tod0317!N%@Xh-BHw z+nrmwTaKdUXft;pY$n4LMKXIGQJJq+k*sB20?3$h9G(+l<}NM^R9f`2BH`)!p`_RJ zoEI?Vw_&CcCKlnvkzqoZb?F7!R2AzV?fmO%1@tt$;SD2#9 z<`2mjE;AyN1joxLAVrNpCpk|o5+A@;7Y)ozf!C26ur_!Ma?uUDYg1u^Bfv&MvZ02w zI>lSRC!d?Y1HuyUQRgRMzl}HIv;EqeP0B|rVeu`MbZY|fI=0_ej_4(j(Wib$Qs*kO zJ>0o9GFwu&EGd8cX}3Cz-=-(e2y?BNSdl~1l$*?~% z#oI6eHe{0t$qORF9AoJSm)#yFjqhm(Oe{|Iq{4D}IeiQ9QQgBJN` zX5;M@Iw6B4L%a~*Ssr=*jjdkjXO*LcVNGw0h^HIS`bny=TmH~NiK@q+i50OK6C9Kg zz8^Fr)@VN4Cj}sTXqRiiP8@by$Q4f#@$@}BJWPfWLkL3Ozvp*(cBjRk$x!)-!ylXP z9L?AoI<^w4GjZ1Hm5HP*>eylkZ)Z4sC1S+BL-^wZNlMaRv$Hs$Lf~7~ZuOz0<`*ah zov>k{5x#lz#usJ)du+Ff*Y^ef6%YuNm6ac`jT~Gadr@3cQqr_L*^<4MCZXPPzXXJC-c&tbFoZ z!*+oFAq^i0QO^-Y&~*r8J6r+a{b!f5ypX`1D3qP;7X@_8fH9^#y5|=ke1868k@&Tz`3$Za}(T4NE%od;IC;k9T)*AWNtrt1Lo8PfIdX2AMhYC-C9OeDLl{!X zj{q=Y{xv-mhbyJEJsH4;4hsugVn6+eGWWkxQm2))FdGJ;@F@n#47L9s4i5eUcu9G= ze!d_Bqn(o+*PQPqj~GSJfads+S)b=izDgAN-@O1YE3Gq!@q~2RES~z@9`AXHk8n)N zD!nu?hUT|N&mNDUW@GS#5?7Ce#!q~-@Qkv_uLn0--t5G$pbe@ba2#VkILChNw@TZ> zkk61$+_Pf>lGDNB7Py5c`NnXH+De(3@uEl=n~~r}BSYSa46daO)asM6=NTV`8|=Az z_HIv%wM_0`NPl2<-(_SP=so2yZr@e=47#CbQs^F~_(rD8G6jUtx}=c$@Qj?)12)@v z!yeTJ{AbFKuB?hSI6qn8zx}Q~1dsTM38sZlf}ekK{&KS;1-W3yK*Ng9twN_0LJFen zZpw-f8?L@(&4dG=?TEovy1IW!XL(q!vy?_Hdy=2axn5%pTvmH2a9Q04kvJZlm%YQw zpDgL9-xWrd*dQB#sNVbJk2qHUVkf+cC4}q2Br#%DXdrRV{W+ebf9~tjt@;MGtW0-Y z8{x-AnY;tx3SMN{Ye{Nsx8!XC=vhACfcI^nm$VvlF9bf$Pr-NR7lZ~$z^G1;#0 zM68j4PxiCg!mE>RO@li3qQA~Z+Ne@3#kG4b;0s|azSeCH@#)5sq*FcKL$`PelJW({ zN+yd(`w1m@?wszZSdw&zg|_ErMV{jwyw^Xw;`(x4@^%Eu`fWV6>)RgMPRz#7^24r$ zMel(RGCODDfjy{CJD*}Fv*_8}OH=pJaMb(3>=Du9qQxkt`E>1t+hI)c9ZE-@?mp~z z(Q!&Vte3~;@Qh+c%absYFx$FcTPx=zP?_}zYeE1}F5`zN0I>j5Uis-^7(0g}rww6C zH@3wGwpEe|wkg|Q`VveuFNTZgcIhXtCZ!Rpkc(dGdKfv_Wkh=JLUm51um3P2Uka-- z?>l(v$qzYBSonST>_i`aM893S-gq1{Tu#&Yd3coT$OM<;W@L-!0cHVX+ttaI>fx{2@QvsaY6wGFxki2} zeCv4WbeP|>B?PDaKD<815}|QiS0@qVBtRW~w?bj?+&Hx#-n*kv1%z61A+qq6oG>!S z>8w)LqI#^n_~FQOM_*OGL5=Y^-f}2p<21t20ZqT%uQ#sMcu!HX>tT6T(G@&rdpUC{eV(k?FR#PFfBxhG!J;igYhHq|9C zIHQIPypKr>!fgoh2mDm75np-NQO5Yp-%mPk%TG``cr0Z5lozU|+NHE1cuXC2kzuUq z;i=K<|p1(INwp~7$?LdXF?;t)OiIJTzDTZ zRZ%v09O!a-V_1<$4!6D1?iqacGHQh=Pi>2JRWd91YE?i%hcab-hN|=JRi1;(ttVDF zdQaj+gKHCi|I(}t%kt+Q(dFxSozYEH5oN=rdF}BXbB8xOspR@qM^VeJe+_@!toCb3 z8EG&E6q`=cZ-2uEXCl^Yx^SB)UC!8U>?Lt9&yakyW}Iwq5UIKSm^xN*mg}uGfikTA zUZs^sQ>^jmqBdPVdTH}}a><#69MAd+^3vxqx6B3duIrGmoCIWWM%oZRqa~wz-}DJP zHpP+?Bo1>s9V~fegH?&~%Z*#bbutw$$00srhMYHydWHSECD?*8?1d*di%P|5kmb~4 zSa~)!r74->H=Z1(_9cbsL(SwtPlBE6~5k|$X+uT z2N7vt2L4hGn?m#?B%l3rfduki%);G@MgNZ{$WC*h@+Y!$Lim;;lq^2fl0DvMHC0a@bNr_~2)PfQ4Fe$zk%9cG`3Ph6 zD*G8zLJxup>|F`&6<6|IXROaW#4bV;2d)h#7{x@b>b$3kH2I2+B$*@md>!l{hCKR- z6NmkPS7ZUixJfNhz}crTTF{O;f;#bqrg>4EjcV++E6RmqNSq7v z-x2&@nBw71L>zFIX(?V5lm8?-sPjy9>-GupS-~KGrrXdh)}h^p#TU6sy<>t^ACTbA zxavIq*3a!OUb^Bk=CMRHg<0IH8^$V=jr<$?9L&pUd@BYGgyY1GKXgWzd(9MoGK#0J zhp5EU{yleg{d7FMu$u7GJyuWHRIn~$&Np4 zF!RGJ?7)*OQ`Ix48F91iekRn2-NDpAv>kLfk1r%1y-0?0h|4G3r+2vXVW~l-VOM5p z`?}mCp=hN(d`&_JY}T_N7u`KpW~z(w`O&Z^X2osW>=;1FLNkA!fr7pa^D5RMTd(37 zVeE=T_JN)_8P>K%Xwz22(a_s4 zs(Id4*x)HEjhvyQEFW`6q||I(>c-)h-hNMrNH}Ia9*Um~#p!T_R^ zs#3>dXrNf0<@&WmySz`;O|25o(HoBQYk473nsa5x=AH*40?9r3f@DSgYo5j>b$+L^E$H4s##MG4#kf~z*Q8E zfr!wVB8NB=dBFib#klE0O_Fcas0ND3cXN~n2(uV8yK=9TbI{a*UZgpm4C#y76c%y6t3@2ab@%Z7__owL%kG|e@x)^*c6D}DY zzMG-E_q+kKan5TBumApmY~zBv!4e{2+2$3!=0hzr$Zczw9abuo}DO(fLjR`kq zFXkO^Kg+7tYVuU5z<(m!$ob4m;Z7*)Tp{pOJ0g^{HHZ$Y{wk;ZXC!&%czlfus?8q{eSso0tLTFS)!w z1<1uol7KkuCObgMTy_HXaY59OxJ8>FGlJ5bX?a<%HYMQ|C^! z2l?Hf=@HIL9xrUVU&%h3HJJ5y?%B=UU27d9aLflTMbt;!>by z9Q9kFE~XzExRU8d0qh@l&Q5GLe$ISuLTn{g5Nh+Poxo4!UPxX~pKjsCsV=r`xV_b) z|7@jco?hY7q3z<W&?~~8Cm)V@I$I|WY zb$GWcdaZ|nJ%|4Pyv9noZ@o781*Ex7@B_7e`(`oHzK~z9prAkqT(Ax0{iqnxlf4cK zumhD?4xmBZxy-5)U8!0e?KK zap6D#ZA{f6dg2lJA+r|#8xP!mpkA%p8W4mg1h{ft(w1Cdg| zaoCkL6rF!JOZL{^mPzlg%=E;`@0=aK;MFOhN@u~h?N)X-D*_zTQMVqJvD;4G1K})s z$wsyLc4a*%FV^#SkPi_lZTFl0F}i3J;hltB*p~)N zr(V+rCxhAedmL|m)Ak8?7!hv%XlG2HaVhn-LDP9VJ3H{M38KJ4%frvl&y>D<3v8ZX zJ5~nC3`qFdByW*8UhBNB8Su0*k9u$QYIK8}7fjZ3WA&m*p4(bYYosJ3bP-@BvxmV~ zy|xx*RS&-GmxyIAPgf9}v29;NX+ags?O#CQ`uc#TW!H$uh{%Ot#st!khMdPzDl1As zwD}-4CB;v~b6-hQ^Q%8&;#V-aW%bDJ95;Qlx3i;37wPL^Tjr2RcU9W zoI~xo`5-s%1JBVRjNI*GCNV4cZyjjp7SSbk`Rwq7P6L7eZt)4pxW=QF*V#ft_ z(3d2|#W5}jt)dVG7Hs7?`t>43OCU+wjyU$)Hd8ty1m>c%d+%PmbKPB#!a#L9GoDwy z{2pB0U0urHfYy7k^5u%Y;o{FiWI^)V&#*roD~&KrT6ATg_*I;BT?~9{!ILlNU~Ze>UgA|5M-K2+ z!_x&Z3)8#?fWrm`nVhX=6;t$RP7ztfUpqP!1YSR+V9xxu4X*A>s~~k*~})vVD6C1WRnORm7D#y%5GAe{Qhcn)+1X zuslB8!?-8}EWfm)M!p_gui){gZNa>`?sOb51xteRBzxDs>PYCHS<#tiGvnC!Gqfy&|2gBb5;&T()+_Cs6_t{q|N*>Smns#4Voc8hzzLTL*;D1nx~}D z@q70J1rEm4isStS*I%KMS#tf^i_6t3GtksC14aSFERehImbTOz@lVym@Re(VPL|I< zpPk$Jh6EefAJ7?+cLQt;v>wvx(iRws6x*)u%6fAnY z`(`EJO{0gx^jyeRYMPgS$a$p8qvhK-tm|xSAt9p274H?mxZ%C~iTW!0BNUL|?_n!L zNWn;nr_#~bPER6s$S6mpp8sQv=`oE64=(@>0tpDqBh`DG-HQA$qpmQZ14#2h?fdsM zuu`R=c6F?Tn}Vf>p)!M8PKQT06MgGyTHOtkO?j!5@+GA zx1);rLgVqKy>Ivn<+})ng5;n#Azb8~#^)<5D}T+*tb_0e%7s(pO)a#lGxP@B)T@cU zAhMhMt>nm@NdC@Q8>|&~?^@ z!6GxZvO#6{^)IzPa@k%+CHwlntB;U};I0xKY7W=GAq_^E{4g%ic25H8G&mdp5fx4uP^Fu=L?Y>r)|KH+i7bKY`5ClJ6Gl>rk_j3jH zesA(h{nyDa0V;L*U?ll>N+ey-XMcH|0XBS$aYfG^*`_ z<6WZMY9yN1nkqzO-?VfK)@+-u^T-#-ge@=(rlU3d!tUES(RH!Qj)vJuscB8RK*&|# z15eNZG@;SM=Sz)yM=Rc&D(*Q?|7shC{U+YJOE4~fA@&_kb2gQ^#jf4|N_=GAnyluE3=dz_kh6ui z>)Z4^kc1wSGaq3Lu7RDS8f4L!&2Rt8pkJ(){{cF>D!@|o999}OBmr^I`H|tp{(GsC zUM*;*BfqnQmSvS5*1bJf0p2Y1Jk%#^_PM&%Jn&>l)5dDj$zGE9ML%!RVCWcnQw9AH zxD1Mk6iEJfI#_~Nw-#iL4K|0IvLhHp>?mC)txjn08i%b-;PVLjeo7&@Z`!Bo@+oG0 z=pdU*Ho}w-3WD7f^bGzQ=z9cz%d6bMeCkH&*)Da^#{IwHXgft9=zs5q`+J27Ar)$+ zQ)y79;iOlBbWp_wP#lw@s zxm@r`ZjyMA(feGwVTyqz!9|qe^X!qI;nDHW7QQe!9?nX6x}=>2ZnhY1i~Q1UBbvRU zzEShBy4r^W(5a|0n^as>#08C0fDC>^3{%z8ih+ohs-f?xRu9d3WKOU0t`7_hfENE2 znm4Cc-k}JBQ3TNZjp{T!gM-6DOM91ziAlTOs}^c@Mrk+jarA=t4(Tp`kf(guE&v*r z%`G>;Q`HCQD;b<;EZ47#p}C+GN8ACAoiH0NP|5OPzy)3hh!%b zgw%%J5XgJM^u$j=_@$}I96GC*Z{BpsE(dc8{A&8wuNxvFdZD4AWbBW8z#8N^ItHCj z&1J=zSzdZ$&0mH#CW4^F$Q)XyvH~Q)0f&r04w0Z0 zz?kO<3Hu;xg(oEBehIj;(C`dPMqWNwy}YDERZ(~=9DqqG)W0{_*_9xfhIAab@9gop z{oM<2wAF6`t{19i6-~_yaI_In2~~&(2b9l#x^l81dh!9xDrSH@+@M?owhqb-xxmqN||QGPhYZv#w(WygJ-~v~|I)q+M(rL_|ci znXP}6)i<>tlRNsjq{k!Ao)g=(ZI4~^{mnVVdwd@~08RL%9Y{s1 zV(Y2yUfBn&`|JRSc+r8MCfLCz#0hwt8}Qf`PhVC17fH2=g{9s3 zg9AaMR+4V$MO0B%o*edq>L2c=!uG?%>L|hXB(Ht5Y3CV1={yy#;@{e>` z@4uvDVq;T;Y0!Y$n6CHF5YG44AD7nc&5#FC32Vav1S&yWFcV0(eYplK-}t2FFJH{S zsX18VTHMv$?YO^bb_DsaA9^V_p)-C@C~se+{$OVX`NeQ#w7Mr*E}I90kHGxD_H%oF z_Nz%ry*_*X>bS>Pt-CYWid&v~{2Ao2u`yWb1XtO`-o1O*z9bn$Hl4HJe?_!k#_)`B z1dpE8a~4VrG8ii#29_H)GMYLtvFcA883Y8hA-ta>An1jHJsp;N7Wj!<+u9uapiQ^i zpO_I44E?K56Wand<34q}llUP=pFe-@aTUFN`!)a=p%Jj50R@!+#>G+~I;6A8!gC?r znS)EP^vI!BG?e#X2wG7k?%!vI!2-LsTD{lNCV&fhFu4xax|efunZBNyY}+;e0HX>Y zdm-RBZ}`x9yx|!VdWKkpgobk!l$73ri>lOR?FslGCEcJkc5C7l^*mV2w_J6$T)hBD zs;R2}Ixoqr=9!gI?jc-1s^d-Y!#WjWEBK73%j(h4P@M)Kb=LjQ-tJh*rz5>`h2{3` zqCpYA7cbPR8Ff=*gNwhRJBpa86E-WvIU7|@(-M~!VhPI{`INq1yI_egO?KPxHS~qQ z2S;h`$?-v~uzMLYk*nk4(&~?hNJ&$BdQ^phQ|jFZ5^sa=Z1u&Chj7Hf%jJqGLgD@2 zAKGs#@+$9qncn8*({s;Q#o^d)rX(%_2OwHlAGVhh2vdNC@wA7{%T%c zb&&7k>sQr|eFwm>88U=@<_x5HU`6D+-b(}wm(_wNeiKO5Y(QXJM1iiz*FL=#M^_82 zuv=YaBgcfW5WSBU^c|On`H|6JW@he!{NgzI8E=;0YwAm3R;Q_rC;(Xt^VuWT(+K=X z5b>-UbX~vinKTBlK(+drhrH}s%@3tnULhf!*^VUZO|R7b+B`Tx;kQoLF1TrbLtl3l z0E76X{+UBJ!Mgq3pJI_$*`$?U7ZOlDWpM+*P)6A=4pvqL z2-N5lAk4BQ0xru)OFsZU))CGm5k5KGfUwjBacZKzJ7}@B+)l2@Xv=k<|8HIF>?ATRV2HCo{~Jk0K3-rI}Ip74BCTsJC}Zs^a70 z$(mYyuAl>i*v#4 z=J!U=9~_byd?J@-pG{*ekFJa_<=HFp{Sa|RlkM(DixPQho>a9aEgt#NenE3I{YF3Q)NOjA^5YSP@Rhib@h0Uq2|61azqsT`FULo>(5V? z7nA??L7qQh27XQc@tErLRaiby7dMQ3LZKRHjsNY9(a(^}G$h&4acrGOb*6A&ouUpD zszCg|`33*!9#IWvPctA~_tEn|Am;zp6ELq}GIj}Phy=(_Fh3?*_nx}gjc z6BCKEAxRg=dE};=L+ULBLCcBU-(Jdw0v7=%Pzr1b_m~$I6@g?@ban+ttXtyGXs=cy zwE+};Wsa=6RVg4cWdY6^gu)ESPA<6YS?Y&SQ34w{eflxd#g#u(4)VvFZ+&}kJOpIu z6ktv*z}g?g&WtKxKb_GA(#Jr=z%d-kuLvo7pzNflql1#XSi2T-BNIKn1oYA=ff&H; zw5SPnyi1Nod^%e1d=)=v8k&O(yq`ZcAxF!CVZ89+5rACeVcrA=syaB7EZ1x(BtS0FuKrIL z&}~r^R`_0)8Qv^pPm+rllYv8!Mk82bIL!`kc5h=Auw*Jwk`;s72)sQxKJw(hiAL@X zCNWp9RvX$etPF$!3ehdp1e0;gKtqKJ1Qz=tIH4mG3NEBP>o~$Rb^Ei54#yLOT$SO> z3wYvAU>wrEf0vVwW`Cz`irC~$cULC>4EI1nF7?>61MXc0zO@&Q1`#T&ee@9%DvO1! ze*J#HNiela*9<(UrjXPEr)7=6IYQdCZl#F;4bGfBYYt6o?E;d)6b}G9fwY^vxM+Dx zuSOGxkV47a+`M#KMphQIHAOQ(PlJU9uQ0K&9D#n>70Q!=9>GrP<5irc;Yt-mkU)rJ ziF@~cLv5H_-2N|tmzFi);aDh&^6HP>a>$%O*&pOdc28Yv?n)k|>nVGqP?gK~D%nX+m6evMi+6!!frw9XZk<2%4bV82_ zDK+)VVsUb^7!Zk}hG%@>0H{t_y~r3ycfs=K0%0MDT2v1@I<_nIQL7T<()YV~0q!Zi zOUkiWr}e&oEmnN_#)8R%y=i7lF|2F17G{+cJj-|?KToxQEKke{N_}||XsTUOw~+xT zwge^YJ{m-tjh_msQ2&kGla!2uRK@=-m|E1T@zbX^P~t}BL4WC&%Ifoe3Mo9gvr~x^ z4iPg?(yej%0ez)IW!I#QI}(N4ivk1B11cP@?AD5mi7}&#fenAaRR-OqWYROB>9RLn z?atO<+kmsWt{Mcw(L~QcaIP<22I^c6@QJ*%w12VXzoD;UOE{Gb$-HTRd$oAH4qa$l zD1=-(r}*HI8FOSI_iB#0E!+x8S{W?O9-Kw`n}Pr-B65M{P#Gs+y0GYI(}%N=@wow@ z7$Z3iilsE@l$inOIsIIU=LMb`}Qh3IQ2IMN%3-25H#Y z6+xfw!p#NfphEa1o#W`pNcGvZrz2Hr$g%Ue> z2wy=Sr^_6?-2(Z4YiX!|PXr(kI_uWXruuYX7Sus3piG^j1wf4a3(yv00d05ykQ^g} zVBevkM|Ae)P`!6Of&pN2nrQW=XY%+#mmv!W#{%xNxQcYzQlA8L`mm!crl2PK_3>vggq2T=rS7E&m{;1nE1q!Nds4QQu@92Ir- zG^jg3PLL&h^=Zj*yaB@#P!QFDzrTOxhL;DhE9ewp6$ksief`>51>eq&0yC-9j^@Z6@pNpCz(rTY;JB2=J+@jG*u?R_z(86JNaDBF+z^@ zVOWq9-47kAEv4fht8Vv2r$B#}>DRY^K|5hL!2kS#ViOi&VO^jK!VO>8LLCHjQZFn` z&D{J}Ee*AS7cQe&@oSLDOv&kL*P6(Y%6bzTfd#$^36D4EH#RoLKYo-7t+5D@qoKKj zCar=N=*)_WpSgNQ4Sk(yaxw4qkJqEzz*K>R0$?aun3ys@9>C#mz0lL@_=$k`H+04U zRt*?#r}d%c`TLx}Ia3iyN%;WrOx~(pd#xrCT9^gBjr8>NX0T|Xi8pkp48VPu$nmbh z7MvHP=gVOYFEVj)spi+Lzd7FVKIva74!FW5V1fpkzv32KL<&-n|7ddMUt3#q1EY(o zvhsVF5*f4|0F{Z#_KNxL^bn_&(WSE71)`3Z2wa5|)#jjk=?5I=1bJvtp!^yYRURnr zLw&ALG^j#`2N|UnI@M!&t@S=&JEO>O{xGRrS}-dD9=iq5=9~=z^BjmXKS09n0nT8y zww%rkvIhj_Zs4fjLwqy`-c7sAGRCTI&$v90A-_HWaz(Kf8vGQfE%noZnG}BBLGTHedGXUkP-q;rUNQU0r@n7ea@0c zl4SamAKJ@g&Z;@F_UD--I3Iq+KifY745;KU5=m6A|ObV2K226BGVJ`kS6 z#`p8|>-7-Owc>r^4xm&EB;|bI6e1E6^Pu4}ul)$ihcdCzKka3@4W{WiWCeR$^rYL6 z8OSLp1S2ira!{to0|2Omz1aq~on>_sNYgWALspy!SeclZNP|3L3?nlGc&v92VXdWQ zWjP_c-iAF3ClC$|gVrzueiy4P=A_1$u&}958)O#123sMn6k=*>YXB;85T&3K63y?l z7+u^BTSOkPD#8FlEarrMZw{!@dYgc^K1DTyusi>TYRELE!){%m;CloF9|y+|ym?@Kg@E~*!^1&Q*@F`AUcP8U@cJ=@kFg*uV3SdGUQ=fbYGDdVUfRw#2d=c90 zyo+txK(@4Ma< zH7u98jn3P)t;S(4tN!uHREAJ6-kI6mRQsk@@$TA^DY3kq***9sXnfW6vT?ipd)09bJI&th#S#vNQB>Pk&*(FQ%N)pFX)*RdK^{%<^neY9Z z@4Y?l-~ITVe_9;pe75&;y{_l$y1wt5*AXc>uB-piuU|g9(o3yA^gp*^3>j3|^(i|6T!C<>!4K?&k4tJ{2|qxw;IRq5}j`G+;{sZM=Q zy-dF~QWj6)DGob#vVOi|MWxrY$6oy2IuhhnSC&vkQ(tE}(oJrhJK7{!dyIGZfNtvK zPYF`=ImLQs>N<A>4SGh{xGHBat+F&w^l6@<=2zif<#c!L>~Tq2R@faQc@Jfd zdc6^k&s)`siMru7dX36{$v!@+Gyj7krTR0h;L5dBJ0x*pis9#F9XrGy28AnL*Z_7c5nJdEBSh_^iR^6!wn@mnJX!js0EC(cT&xk3;QR< zXQ=MwQT4iirS?i?K2Ng+RZ&pwmvJd4k1#I{?XeQACdpo7i_071$_?(kx0CCerVRQg zgbQ&qc<;=ZQn&EBaa8m5pFdqsePOZKq2TRY$dALyoL^O@>ji<(~lTeY}2-%lnQ5w@LFa}p7%b{ z#LY3x51l>L^zr9&BG(w&qJ56xEH%~a}T` z%g+1E^Qnf7&t`1=I0FD8mu zR%WrGyn91+?QaffX1XM)UK4GbS8ck!%al#+3RswBKk-_C-pSNlt9!r0T*5NlQvC9T zXNmT4300zPw-`B9nfH8Lcs5)^acmbE)g14i{nLV3!5ZcPt2#Rq=51)VyN^*9+O{-v z^Ezw}&yoJMAnxh9?KYvz4fP4zoCz!A^)+7en+aXYob0RP(;3WF zc`lw(7+LWxulbQ;G@80nvEs4G&Fm9RJ2q?hZn3$VFwSB)8z&E+$v}ymfg)9w$rpXo)_3&*5B6hzRt$ zNN+7)vBp@($8?iv`HF+Z%zy_QAFrUMTPzMdVAt|gh&K7gR)jsXum6lS{lX^GpP8e4 z5@M%q-}}WEwY$YhioI0}4HdL2ccwGh;-{K+6eTqbmsGB(TJif6CCmzI$lRb;%*V32 zbx?@C?)7b%h61g6QJU0c_h(T-BgWiriao`fHCaC<-7#oN)7DkYvslcf6CQrKe8tfk zZ4p1?CV5jKG35i|eYHBAePV2Wqh=k?aYjyq8f!tu&3nZMcOKDu;CEBzz_22%^1J!A zVy_@~<^COgzO<}c@}b$L^%tnI8_h0Uvbg-zl5%!GXvkbUzsg5bK8_=l_t4IMnO3? z9to9kODk4n$!91QRknz*ZLVc-Jrd`guTImHRchLC_kgI0pXT_^50b1OFDbGfA8Br0 zCowKh=iU2|Ww`tOFK!{3+R^)qt;KB;6H3P$I$Hww3?38`(QSId`%c9mB}y^5a&Xv# zwV?Vni*dGCh@CVSR~a?03^#{9Qg=T1QvGO}Xg=V{J}~u#X>4_NjX<<1DMXnPZwNrJvNx zIeDAk-kuXS8r6&R2fy&WAiRna@tTtzlnQb{-r|q zrfq$bE`Fa%dm5UZCnoYsa_)2r<3Y|mKQHO&`|P4D&o|aT`wH1@7?L#VbmjTSah|`# zIzY$u^o!$7K8fn(D;S-PlV-;nDTCLU(|7r9xOF9_vwmFJc<0tzWB+ZmEV~iW*8Spk zx+l~owKnNIf2TLH`B$p0!l*Fq^`pf!SrtEZ&0Nv(r1BNttB7&t6E{jKq!oUg7L{xr zJ^nNE*X$8~`{P|pHFmLrODlCA*a(YVuNtW{aafT3baPy8KYOpYcZX6%Ye~gY-9^s~ zY^NN#2faQ|QTMDX-6D68QKGht3%3VzmD*wBh1nX~eq4ur8ZQ-1v83oWZ{7h+Pm$5Hm8P|jK!3bL;ZnU?44X|9`Jxzyxg&xp=Rp1n0gS5#Ve z*iDUYYmxl82zbNq-y*%R16#^94Q|VmEviNPb}$w#NTI3HG}O2!KWrdzQ1wc(MYHz3 zo*+3Rw~V4@aFv;SegTEzOzsPL{kE2zCwZN|BosrT%liXlQOr$wdDLC>h@nW?f<55i*Ni}iTr=$<9?i#!{@)!yGXCVWr$yy zud@H$@L`1KVw=8fVe!*Vp9Pl{%a@brsw9Bx{2+=k-v_`vBlk5(pA%oCTDRe4}}-w;2_vnHaXTIlf}Dg)pyjWJq}UGzv^}Gfxc(We0_U)H~Ch*Z^>@n zf8#$AK532I%a)xj9$*Zm8|8ftM<76hpC{p}DAaRAoi0r@hyzfWkm*_@Adm?tiKOHq zDf+!WR-rL~Ine2H-<}N{|FIv)hJ-<3%N%6Jpe->)|swikRmnByadY zq*0a{a1K)!WE1=J-{VB%&XDY_mB~{Ve;nc8bLe{GAlCbl=zHMy)ru9`5K(~lHtU}O z??f&ghor&M^z48~8z3oKmXD~=3pM;n$!6ffiWe_l%!m5c-XHUKYV=`}JoYpd5;=6p z6bR|YL%xM?y1Qei$=4tualYLOs1*?YCZfE9!?i&Lq1{iF>A{=#-Ffxuw7CASBw=At zc>ey%&3oe;8XA^#n3{43R)%DGCQ5>x0&LYpN+139o5Em6K`u@{ntdAL4#W>FG^!wn zDF@CNVq!F8L2VP0q0!IRpr%!MH@*1dq=Ns7SqtNv0#n}KQKE7bXos#l z$aPYyJ_&`Jj?YjV~ODXACAyURiZJQxlwtWsrCdPycYc@N~PACTT~V50y|v+{26 z%^y++|03`{eQByO3&YL7YQW=?P~Ow4qPsQAhxVgpdmu2FyNHdQ8DM6cly{@*+BrrG zUwB$Zeueghbq}8#bZ-)8zwG^lv!h6_{qo3{{`-7NdnZ&h86*XAg>FN`9o3f=6g&zA z#;rIcIG7=rYx(m(k~n$d2kdu1ZBu#;_cxt00-dnY{sIQ74q0<3hNyA`{iq4--#9q*&na#sQ>>>2)iu6o znaHHP*5%)@q3^ygMD|8_YkH@DQ($L`hfS`#$=oS);D8}i>3k?b4LNptL_&fKKt3p4 z0%(x!%nneF7&0%wkKO}aBqA%@?CCm-WLREfdhP7^gRabyIRknFvQ4c6Ug=OH^$rZ1 znVEtANORZ7i3~x;*=I(&^i9nVz==NcZCJ>mjQ?>j$2QqVKft%~n56TLeqG%S3mT!5 z{zBLWh@+|39> zn7}#rK!ng3H_bcLp^M+I8N_1Qkb9*d4@dIpQ8Z8w%>TDTL)Q>34*7-`s7;>C|gXb|?vaa_{FsIX`y5jF|j3SkqjHGlP(y|?ij zqFc5c27#6%FOO%n`WubcN41^+hmc+z#932AJYU;{gi4{OMt=Bk_60y2DR@D${N{ag zy+9viUikipoMwDqM}IM_A^!phTwlNI@j=u@1W4dHlF;^I-Bv<3kj3RQ-4JW}SCqo_ z0)Z&(()M7+t47&2HDo`r@PNM~&s@K@{ADR&;pC_8HMivKKryKy17QQ4g@}CRH8mLz zjD3djWI+V=^vv@_AxE_f7YeWb{PWITyLOTE%{y>*@&fBjGcEysi2x@Z1+wS`I9(|$ zHvm1~>y`~Y{Qc?Eku~b`uSKxMlHRh&+S(R@3@*XOc$q6mNJIiZol5tc*@=-AM5*>8 zQBL(rvVl{$yU%Z~kl%Y?SJ3)hN1&tD1CsS1>l&N~aU}r+5NPryn z`(*E$)vNE$e+rog=r8wn_%@~pip=LEe9$&&>Fmy1N602mdTkfK5FSVJl9u}S_gVqV z@Ft)<9xIkD`!Kl^|M1^-b;D=WmOv0>>Z~gaQ~jZT1AcV+(w{&1l2O3u`(Hy3Z~*^Q z_ruTrExb|PVJeh9Ir&}T*)+keef|5hZ*1%9VxNJKhK=6^BGwKCD|3=dmOfYhqHD&l z{k)_6kY)1+z!7g_zGS1nz&!@NUdZ8|rti&!L3)j{C?izTh8}>(w$gEVuQqitT|$|2 zEn!;6N2JG3m7`lPPM@HP-pE$1cjIp@(5?TAvew_RwJ$5sJy$uu!s>eXs6%N)vU0!V zq)$jn%Ifdf`a@x3P7-aMJ5oj+%Ma zwb16>8==|5T}w|&7F=jjZ>qgMPN$2R@ThVw2e@b4JMWVcwJ=+d)_Wl+;S_zXZuYQ< zg4j*BLpgS0@|8Dvlk)m??#~hpEEKIY;@i(G64o30c#rv;?Wxk@rU`y)W6MFi2TcC~ z(eQBgp@egx=Z@45i*_nKGw953O;mTH3TR*N@lRMPl#^Z>ank!DE7&g&2N&P6k)(rz z@mC53qWIXV_$iHu1RBfAZ3ETsrnF*H?Q%UTZ=CR_x(QPqSAWJpxx|pIUHO^LB!1#R`U(c4$#TKiBjji_7~o z%A;QM8kQAL>y6nMRdlA^&bT2{kX9egJ3shVld+@Em};t*>Tyz6!LTns(AQ|XC?AI|bQu=Hfz^Rb5^- zh=#u;iDR>c z=|3289EAHU!Y0y+os`7{bLhNR1`AJgtfEcxO4yl3${=!~yb4q#U)8oHHH#SyJCR#0 zYnJ}5(g&Atki}peeP&QELXFIMpKGLR%}Yxx?DuvGE6;aot7KV4-K*TT zwPEj^T9e`e`Ousbqa}7JvdZQ(v@*_l;jiivl|ZsgOJPwGuYWmi=DCHll^ zvYGQnGF>Ii6ZLzjX%>sm9?p615ze#>&$!ZjCsxJpM$V40UlKIZ3X9LCPTpYdtZ4M# zKV}Gat0_EnG?bZ`nM;ilV?SD+ar>nk>uI!Ds?)Jpb^1_Pb^-rWG9ytwaPfSjp!Gy? zQ554ewcD~IqD1o^GkUN~+-{;pn>M-LJGbJ2L(klr8a0Vw#9Cvw z*tJ+o=FKar*d7zGk+5W2R!gMCaLJx%lB@SM2yl*fJpVI=;@7PAlch#2klwda$M^iPyVm}Icj4$!t(=<;!n&0>Oo z- z#gjktX@6=l|7kSOf7@r=9=vDou-T`#Fox*{MDX|Ir0ld4tm4kBD(r*3Q zUxU`v+DGhjz9Ylw7x~cjri=sZ&ir?muDOd~@<~LRu;+fk58k5kn9r8VuA2&qKa5 z-2&`&C7+H3ncw5&ioA20BmVA50OhMTw_hu!Wbcp*{q>w?&2{yR~)efK}J8@aKGl=OG(m$MPTf<|nY z-GFuxlHLog@CaBBLW|QrOxP{>+&~kdD!Cn$mj)Efy?5E;ABe9^;A6dMkQ(H3d&Qlm z$Ki=VJ&C__HyOri$WI*wzpe+>g&|>luctDcjj&}SG!{f4Z(sP5>dD05P<9_I2j~$6 zpjR2=bFId;I)u*#$I%QE6hK(B21FTS2`x;9UIpdGuDxcxIqs*GU?f8F zy_MiOZ2nb<01PJkhY$f71DoOoQo+Qa08j5&UKgsPtfy|1l&p1cQ_)H~u?vuqjPH=0 zcW1s)iQ7JbTNV};>#0_N&}&#AL_mVUmqc?%`-SND@2y?IYMg0-rl$rV8dX3NHKsrQ zVL$4X`6Vo@=b#=`n{9%Eze5zN@0}fg=Oqt#5jLO@3o;)?`?l!exmqu9Mi1`hYl zcyVs+Id+jXao?RPMrr70l5o)*^E-~0N-f=6pP)?wD8Prou3bDg?@7@(Eci1pM#wnb9xCQ$w z(J+CDU^1jqZV)DXePBu)ToYo&0Nf+Gb?cKn9vF!{@wJKUg|nK--kJ{qXJMA9=CVyP8s0;0H|u@c6t%I zKN+pM()I5jp~tAse33+mTP6pwzd?zq-)xRxDhN=S;OaJCA+a@ZRq9$>r=VbkyxU-_ z4y3?Sj*b})(=w^U6NNp4Qy-4~RYXy`(Btz2ip)89J22^DmV8)51UWS9e4IQ7r8ODT zAQ*caT(SP<%(%IU+>+G7LL)TbXoAPRKfA=e*)?wc~CCeRaps0|_JBgeqH5@;GqqbSUd5cL}R8`A(sNese-=Wtx#VWN4+D`PCW zYvwq_+y=A}WCEY_j9E;=PKsOU*V6G<$r3SIOF&`-_o^Tk-Q;tD0A-?>(n$y{HLRG; z>?R?Myc!mr{%vlL4}A>Fh_+oO{8^2T-woR=K^<=T5kqW;}a#k~|KCa@DmI<0$ zbrD9;$h6GjR;K>_$4n@XGh@kzWC$Y#b(r5N|JKc$THR{R9s@7U@EBWS$*aagQjIz% z1c0Ps)`MQ{(iBZR6W6Xu!51Ne8*GKOJP9gyW2+zNxf8I#(Ey-98z(}=9>}8U6RWt) zuTRy^&uFav_S+xG!#j$0d+lC~NOQ5E`tp1@L6jsAHD zhbFPOIYHa4`bx6!G=}dPJjSQ-WZPnhOEhP~t7-_@miREz?XhTLUItysm__{s*+I}* zSPf*MIPdYB8;v8UQ}xify9zS&>rr3QKP3qQ$2(MlcN@~fNKk{Lvz>qE>&iJVSGfZD zqw)2Cp#v(GF8hF3!dm~}Q7o>Xc?UIMQaC*s{EieRa>8$I6A>vNXv@tsFm_t7)cktO zuV$Bs^MnX95J7f=eBXnS;pq1Fs?NmAx&Pa;a=VO@4z!mDOWWk-J0TiIL`1BYf(+;i zqfy*+2Z-0a!3S+8P_BYFU5HNymzFVV6QUEwU_@bDMqv{~uDoC*#H;3fIVa-`Q}eMJrQb2xS)Hi0oTLmz(2;9&0=1f<<)vHJ6 zb3Nt{n>1eT{H^P&R<*BQ9`n6BaQT!C^IEu|U!D1{iwvq33fhE3G>RU)DIccR|FX!{ zJ8An*kx3%QZ>QQt@T`~FUVd=H{?``Ija0R4r0=Df>^|zb=6FBt#Weu;-30|al@b9R zVI9K~(_YmvW)q{cF|P7mPo{ipA}24@Or2Y=;65?!E~_9GcqihZr;Dn;`{*uC?8LDJ z(($bbGU@`B)xEd#l2KTgKZMdp(hIobdzaTy)}Jl>;!{!iLo5NYLPp1b@Xwf&gG+u> zm;~FJgjZ`d`IX?`mVM4sZZ})BQM8zMOW%iFSm#!b(SCS2&MH;w3Tms017FS+wMl++6C zlPZ^_&q$Q_(qmj2oS?*0e%rN6ntJ2yyl%l3ISXaFM$+zkY04u`R7S!`5zn|rbzJTK z1bb)KmWOu-x*xyunm?=DQuU5yqeROUn~sgKI41h2%WvD1@2HoIQj3z`=#X~te0QVc zvYqkVDv6T&^#`v|7B;nOb8c7TKPh8tx7OY3E=s+=XF4wRV1}oaYl}y{^XZr0{+jbC zcVow7|-6V;yBGf(IZe2H^x>V&`pUwX`W<(5Ojv@i=T`g~MHLB*Wm z=o#V7nob^~A?J&u-UbUE(DIxa`O$G`@}v`0GrGcK|E{v(_at;XP5B0&`Gop3dE^$$ zLvTumK1g*`nSO2@>t;#Mp)zEh=6O;Vo#tzas;HZ8)#mRzK_BmEchX$mRUG?vUT~^e zuy)%%+43>j`)c?!fBD1yx^kJxBO`N@!uz(C&#U(c&D19D4rHc%wcU(|LgCJ)9zCqx z$*>qUvm6a()m#)jq#<;EQ(Q#?m+{At)J1MQLi-MskEbs3Z%)|lDvXa*C=pg*Z<^a0 zrnFN-)rCecaWmgEDV+Cxc>%ppuG~*!pP5?zOY_iw<;}qtf1pSd1ef2qo7^7RAGfursOsR_r7PcuIwf&R zrsj&<>wE6591C?$^^L2GwDiI!(U$yfzsS?%iqu^(#n_)y7oZs_mSwEYd)~R$ZdxzlzW-GM+;EMnGneh2muGWIi z@eI=S#-RcFR071W zZ>wJ7&u_kHZw?c)uT(eUY&P7lCz0Y)WgiGsaVK!@E1~U60u*V zyQ_cqGg;I}Dnw)7YQWm$1O)T?@1?0InUZekgX!t@p z7nr}^BD%6yKQNe$20sTu2RM>|VEn1xosj%jKYq2_`OWVrZ<&+mHU;M% z4ododyk=u-Tfu|=QmFNDh^nZl7!zp2V}OOn34CP+GYY33K9 zQY&-4tfSF=Sj3uGa4zuz4iH1hgWx@GacH;wJ+)xgqX`lEwRgh>X;DjK?&eET?}PnU zpDnrN-fB~d)crN=Zr9}G>+7BEfp;D}ct8p$8nS)H?h1&*pRHAh$}j1ba58MC zubJ%f^RsJ#o82hJt%+6&sKT(4jIQsgA7MDmVYv0Q%aLKoaCNbg;o1B3&aM-hOw1}; zB`3LH0VIitDxP9 z%vp(D`X(Ju7EzRR8^5u}7G2%g*jOGFb9)h~)Zoe-VCIq$bxZC*a*T_LT??WVq=SE7 zDWFBALxq@7j_&IMY;*&mFu(KeiwFsN3P)?~M(f*nF1%VurK1Z{LJkQ_P%YwkbXm^I zxKz=;&b+V24k19TLSd8`JVpuk@9)A}DwE~MQ&Et2E5HS>@Z3pMdrJWSpe7(?*Th`O zF76R`Do0Pm4$dS(7`nDqrQPfOP`aJeLl#*>#*!adxpa2kICzt+1Sy-RzP~TQ2+m^+ zyjs`?N#(?xrhNbukZv!v#~kM7=H|fYnjP{W^&$_n*P?o#*Ff>AvM?c4h6SVqPt=cX z^=WEo2!+=pS7Z~K;K5x-C-sD>bbQ}C48ID9WvvaH_PJO@mlSvpEtq|Qfg^>nc>%Yj z7NP8|Hpc?-64*dh&b%2=Ru$0F(`!ze`B0o%3dd^<`eoi9A7-%akxDXBx;XTHVD=#8 z>osz_zv?xFK&}v?Y9|*sU-EDF zMp)b1S2^T%G!0#TiQu`KtO2O)C4)KIg>Hd)k4i@ci`<+}a^GH>hvd*E>y!=}?)5M` zO}sNMC?T0ELfSxPK`Gi3qUPR&$0C-Hcn%d%LmoNiYM3rMr{giyi?yZ+hiPtZ4mKkL zYNBMgsjnyTgENitcAtrHKF*#_pj)7%6b%v&tir!wIen{v8f3+osC#{ey-lMx%|KZu zuA;n8Z?3rlKRJWiNCZ1@?edwlgiUYinUZ|{YST;I`uHB$)!xnYNc!1m`J!5sCHdrE zPal@YwA71Pp$b4CS(8Xj!kkXHnd2~UtRM^LjZ}z@zPiL?g+*m>qPzX_MKjd3-Ukd! z%5QE4)r;g0pu1^q@p$_6jWVL7Z<<{ZB;*IdCWwLkOz@n~d<^kBK}Vqm=J>8^)OXtg z#-gH3e&YUxH>=~Gk=fDxqJiG~YyKJi1G6(I$oYaJQHsZyD5iOMpy=A2iQBW6o{sOU z-w|`{)Ed-(mXIj?@c0LKY)gN?y80TK084RSnv-gvLR{lPnbuO&l?=q<&WrmlDnC`O zu@;ZMO)gn%S^B6ru?x1jI~f@nr1oSWs)!_!+t=D)7b$0tyKH=mD`k7toh4814pN*3{blcud%0uATnFo$lNPBKE?&~>UWtMq z4M{^t&X~kFKizQ5!NFnInVOh4*!+mov-6Him=+hNq^CP%bwYH)f&yftA-(v0wTV*o z7pK?JZW7>NE1Ak?pX+H#%;lP`*~i=e{>5(;sp6 zH9BIuUT1$28!qzl!6A`9|AWx~Z@1j9)^A_(ip7rDyw9s5Hg#@aeOqOfx$`T3^(m`+ z^U=e@mEy+o{R$cz!uuze$I=*+4S-c;E5GcA$#?!gdIi6o=ZX)YBs6aMS)Y6b^_cq6 Jj3cLi{67My(TV^7 literal 0 HcmV?d00001 diff --git a/cmd/clef/tutorial.md b/cmd/clef/tutorial.md new file mode 100644 index 0000000000..d59e08ac7a --- /dev/null +++ b/cmd/clef/tutorial.md @@ -0,0 +1,198 @@ +## Initializing the signer + +First, initialize the master seed. + +```text +#./signer init + +WARNING! + +The signer is alpha software, and not yet publically released. This software has _not_ been audited, and there +are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software +unless you agree to take full responsibility for doing so, and know what you are doing. + +TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE! + + +Enter 'ok' to proceed: +>ok +A master seed has been generated into /home/martin/.signer/secrets.dat + +This is required to be able to store credentials, such as : +* Passwords for keystores (used by rule engine) +* Storage for javascript rules +* Hash of rule-file + +You should treat that file with utmost secrecy, and make a backup of it. +NOTE: This file does not contain your accounts. Those need to be backed up separately! +``` + +(for readability purposes, we'll remove the WARNING printout in the rest of this document) + +## Creating rules + +Now, you can create a rule-file. + +```javascript +function ApproveListing(){ + return "Approve" +} +``` +Get the `sha256` hash.... +```text +#sha256sum rules.js +6c21d1737429d6d4f2e55146da0797782f3c0a0355227f19d702df377c165d72 rules.js +``` +...And then `attest` the file: +```text +#./signer attest 6c21d1737429d6d4f2e55146da0797782f3c0a0355227f19d702df377c165d72 + +INFO [02-21|12:14:38] Ruleset attestation updated sha256=6c21d1737429d6d4f2e55146da0797782f3c0a0355227f19d702df377c165d72 +``` +At this point, we then start the signer with the rule-file: + +```text +#./signer --rules rules.json + +INFO [02-21|12:15:18] Using CLI as UI-channel +INFO [02-21|12:15:18] Loaded 4byte db signatures=5509 file=./4byte.json +INFO [02-21|12:15:18] Could not load rulefile, rules not enabled file=rulefile +DEBUG[02-21|12:15:18] FS scan times list=35.335µs set=5.536µs diff=5.073µs +DEBUG[02-21|12:15:18] Ledger support enabled +DEBUG[02-21|12:15:18] Trezor support enabled +INFO [02-21|12:15:18] Audit logs configured file=audit.log +INFO [02-21|12:15:18] HTTP endpoint opened url=http://localhost:8550 +------- Signer info ------- +* extapi_http : http://localhost:8550 +* extapi_ipc : +* extapi_version : 2.0.0 +* intapi_version : 1.2.0 + +``` + +Any list-requests will now be auto-approved by our rule-file. + +## Under the hood + +While doing the operations above, these files have been created: + +```text +#ls -laR ~/.signer/ +/home/martin/.signer/: +total 16 +drwx------ 3 martin martin 4096 feb 21 12:14 . +drwxr-xr-x 71 martin martin 4096 feb 21 12:12 .. +drwx------ 2 martin martin 4096 feb 21 12:14 43f73718397aa54d1b22 +-rwx------ 1 martin martin 256 feb 21 12:12 secrets.dat + +/home/martin/.signer/43f73718397aa54d1b22: +total 12 +drwx------ 2 martin martin 4096 feb 21 12:14 . +drwx------ 3 martin martin 4096 feb 21 12:14 .. +-rw------- 1 martin martin 159 feb 21 12:14 config.json + +#cat /home/martin/.signer/43f73718397aa54d1b22/config.json +{"ruleset_sha256":{"iv":"6v4W4tfJxj3zZFbl","c":"6dt5RTDiTq93yh1qDEjpsat/tsKG7cb+vr3sza26IPL2fvsQ6ZoqFx++CPUa8yy6fD9Bbq41L01ehkKHTG3pOAeqTW6zc/+t0wv3AB6xPmU="}} + +``` + +In `~/.signer`, the `secrets.dat` file was created, containing the `master_seed`. +The `master_seed` was then used to derive a few other things: + +- `vault_location` : in this case `43f73718397aa54d1b22` . + - Thus, if you use a different `master_seed`, another `vault_location` will be used that does not conflict with each other. + - Example: `signer --signersecret /path/to/afile ...` +- `config.json` which is the encrypted key/value storage for configuration data, containing the key `ruleset_sha256`. + + +## Adding credentials + +In order to make more useful rules; sign transactions, the signer needs access to the passwords needed to unlock keystores. + +```text +#./signer addpw 0x694267f14675d7e1b9494fd8d72fefe1755710fa test + +INFO [02-21|13:43:21] Credential store updated key=0x694267f14675d7e1b9494fd8d72fefe1755710fa +``` +## More advanced rules + +Now let's update the rules to make use of credentials + +```javascript +function ApproveListing(){ + return "Approve" +} +function ApproveSignData(r){ + if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa") + { + if(r.message.indexOf("bazonk") >= 0){ + return "Approve" + } + return "Reject" + } + // Otherwise goes to manual processing +} + +``` +In this example, +* any requests to sign data with the account `0x694...` will be + * auto-approved if the message contains with `bazonk`, + * and auto-rejected if it does not. + * Any other signing-requests will be passed along for manual approve/reject. + +..attest the new file +```text +#sha256sum rules.js +2a0cb661dacfc804b6e95d935d813fd17c0997a7170e4092ffbc34ca976acd9f rules.js + +#./signer attest 2a0cb661dacfc804b6e95d935d813fd17c0997a7170e4092ffbc34ca976acd9f + +INFO [02-21|14:36:30] Ruleset attestation updated sha256=2a0cb661dacfc804b6e95d935d813fd17c0997a7170e4092ffbc34ca976acd9f +``` + +And start the signer: + +``` +#./signer --rules rules.js + +INFO [02-21|14:41:56] Using CLI as UI-channel +INFO [02-21|14:41:56] Loaded 4byte db signatures=5509 file=./4byte.json +INFO [02-21|14:41:56] Rule engine configured file=rules.js +DEBUG[02-21|14:41:56] FS scan times list=34.607µs set=4.509µs diff=4.87µs +DEBUG[02-21|14:41:56] Ledger support enabled +DEBUG[02-21|14:41:56] Trezor support enabled +INFO [02-21|14:41:56] Audit logs configured file=audit.log +INFO [02-21|14:41:56] HTTP endpoint opened url=http://localhost:8550 +------- Signer info ------- +* extapi_version : 2.0.0 +* intapi_version : 1.2.0 +* extapi_http : http://localhost:8550 +* extapi_ipc : +INFO [02-21|14:41:56] error occurred during execution error="ReferenceError: 'OnSignerStartup' is not defined" +``` +And then test signing, once with `bazonk` and once without: + +``` +#curl -H "Content-Type: application/json" -X POST --data "{\"jsonrpc\":\"2.0\",\"method\":\"account_sign\",\"params\":[\"0x694267f14675d7e1b9494fd8d72fefe1755710fa\",\"0x$(xxd -pu <<< ' bazonk baz gaz')\"],\"id\":67}" http://localhost:8550/ +{"jsonrpc":"2.0","id":67,"result":"0x93e6161840c3ae1efc26dc68dedab6e8fc233bb3fefa1b4645dbf6609b93dace160572ea4ab33240256bb6d3dadb60dcd9c515d6374d3cf614ee897408d41d541c"} + +#curl -H "Content-Type: application/json" -X POST --data "{\"jsonrpc\":\"2.0\",\"method\":\"account_sign\",\"params\":[\"0x694267f14675d7e1b9494fd8d72fefe1755710fa\",\"0x$(xxd -pu <<< ' bonk baz gaz')\"],\"id\":67}" http://localhost:8550/ +{"jsonrpc":"2.0","id":67,"error":{"code":-32000,"message":"Request denied"}} + +``` + +Meanwhile, in the signer output: +```text +INFO [02-21|14:42:41] Op approved +INFO [02-21|14:42:56] Op rejected +``` + +The signer also stores all traffic over the external API in a log file. The last 4 lines shows the two requests and their responses: + +```text +#tail audit.log -n 4 +t=2018-02-21T14:42:41+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49706\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=202062617a6f6e6b2062617a2067617a0a +t=2018-02-21T14:42:42+0100 lvl=info msg=Sign api=signer type=response data=93e6161840c3ae1efc26dc68dedab6e8fc233bb3fefa1b4645dbf6609b93dace160572ea4ab33240256bb6d3dadb60dcd9c515d6374d3cf614ee897408d41d541c error=nil +t=2018-02-21T14:42:56+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49708\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=2020626f6e6b2062617a2067617a0a +t=2018-02-21T14:42:56+0100 lvl=info msg=Sign api=signer type=response data= error="Request denied" +``` diff --git a/common/types.go b/common/types.go index 4ea2d56a69..76e7be58fe 100644 --- a/common/types.go +++ b/common/types.go @@ -23,8 +23,10 @@ import ( "math/rand" "reflect" + "encoding/json" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto/sha3" + "strings" ) const ( @@ -238,3 +240,63 @@ func (a *UnprefixedAddress) UnmarshalText(input []byte) error { func (a UnprefixedAddress) MarshalText() ([]byte, error) { return []byte(hex.EncodeToString(a[:])), nil } + +// MixedcaseAddress retains the original string, which may or may not be +// correctly checksummed +type MixedcaseAddress struct { + addr Address + original string +} + +// NewMixedcaseAddress constructor (mainly for testing) +func NewMixedcaseAddress(addr Address) MixedcaseAddress { + return MixedcaseAddress{addr: addr, original: addr.Hex()} +} + +// NewMixedcaseAddressFromString is mainly meant for unit-testing +func NewMixedcaseAddressFromString(hexaddr string) (*MixedcaseAddress, error) { + if !IsHexAddress(hexaddr) { + return nil, fmt.Errorf("Invalid address") + } + a := FromHex(hexaddr) + return &MixedcaseAddress{addr: BytesToAddress(a), original: hexaddr}, nil +} + +// UnmarshalJSON parses MixedcaseAddress +func (ma *MixedcaseAddress) UnmarshalJSON(input []byte) error { + if err := hexutil.UnmarshalFixedJSON(addressT, input, ma.addr[:]); err != nil { + return err + } + return json.Unmarshal(input, &ma.original) +} + +// MarshalJSON marshals the original value +func (ma *MixedcaseAddress) MarshalJSON() ([]byte, error) { + if strings.HasPrefix(ma.original, "0x") || strings.HasPrefix(ma.original, "0X") { + return json.Marshal(fmt.Sprintf("0x%s", ma.original[2:])) + } + return json.Marshal(fmt.Sprintf("0x%s", ma.original)) +} + +// Address returns the address +func (ma *MixedcaseAddress) Address() Address { + return ma.addr +} + +// String implements fmt.Stringer +func (ma *MixedcaseAddress) String() string { + if ma.ValidChecksum() { + return fmt.Sprintf("%s [chksum ok]", ma.original) + } + return fmt.Sprintf("%s [chksum INVALID]", ma.original) +} + +// ValidChecksum returns true if the address has valid checksum +func (ma *MixedcaseAddress) ValidChecksum() bool { + return ma.original == ma.addr.Hex() +} + +// Original returns the mixed-case input string +func (ma *MixedcaseAddress) Original() string { + return ma.original +} diff --git a/common/types_test.go b/common/types_test.go index db636812ce..9e0c5be3ad 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -18,6 +18,7 @@ package common import ( "encoding/json" + "math/big" "strings" "testing" @@ -149,3 +150,46 @@ func BenchmarkAddressHex(b *testing.B) { testAddr.Hex() } } + +func TestMixedcaseAccount_Address(t *testing.T) { + + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md + // Note: 0X{checksum_addr} is not valid according to spec above + + var res []struct { + A MixedcaseAddress + Valid bool + } + if err := json.Unmarshal([]byte(`[ + {"A" : "0xae967917c465db8578ca9024c205720b1a3651A9", "Valid": false}, + {"A" : "0xAe967917c465db8578ca9024c205720b1a3651A9", "Valid": true}, + {"A" : "0XAe967917c465db8578ca9024c205720b1a3651A9", "Valid": false}, + {"A" : "0x1111111111111111111112222222222223333323", "Valid": true} + ]`), &res); err != nil { + t.Fatal(err) + } + + for _, r := range res { + if got := r.A.ValidChecksum(); got != r.Valid { + t.Errorf("Expected checksum %v, got checksum %v, input %v", r.Valid, got, r.A.String()) + } + } + + //These should throw exceptions: + var r2 []MixedcaseAddress + for _, r := range []string{ + `["0x11111111111111111111122222222222233333"]`, // Too short + `["0x111111111111111111111222222222222333332"]`, // Too short + `["0x11111111111111111111122222222222233333234"]`, // Too long + `["0x111111111111111111111222222222222333332344"]`, // Too long + `["1111111111111111111112222222222223333323"]`, // Missing 0x + `["x1111111111111111111112222222222223333323"]`, // Missing 0 + `["0xG111111111111111111112222222222223333323"]`, //Non-hex + } { + if err := json.Unmarshal([]byte(r), &r2); err == nil { + t.Errorf("Expected failure, input %v", r) + } + + } + +} diff --git a/node/node.go b/node/node.go index b02aecfad1..bf6e9a7c17 100644 --- a/node/node.go +++ b/node/node.go @@ -306,47 +306,23 @@ func (n *Node) startIPC(apis []rpc.API) error { // Short circuit if the IPC endpoint isn't being exposed if n.ipcEndpoint == "" { return nil + } - // Register all the APIs exposed by the services - handler := rpc.NewServer() - for _, api := range apis { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return err - } - n.log.Debug("IPC registered", "service", api.Service, "namespace", api.Namespace) + isClosed := func() bool { + n.lock.RLock() + defer n.lock.RUnlock() + return n.ipcListener == nil } - // All APIs registered, start the IPC listener - var ( - listener net.Listener - err error - ) - if listener, err = rpc.CreateIPCListener(n.ipcEndpoint); err != nil { + + listener, handler, err := rpc.StartIPCEndpoint(isClosed, n.ipcEndpoint, apis) + if err != nil { return err } - go func() { - n.log.Info("IPC endpoint opened", "url", n.ipcEndpoint) - for { - conn, err := listener.Accept() - if err != nil { - // Terminate if the listener was closed - n.lock.RLock() - closed := n.ipcListener == nil - n.lock.RUnlock() - if closed { - return - } - // Not closed, just some error; report and continue - n.log.Error("IPC accept failed", "err", err) - continue - } - go handler.ServeCodec(rpc.NewJSONCodec(conn), rpc.OptionMethodInvocation|rpc.OptionSubscriptions) - } - }() // All listeners booted successfully n.ipcListener = listener n.ipcHandler = handler - + n.log.Info("IPC endpoint opened", "url", n.ipcEndpoint) return nil } @@ -370,30 +346,10 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors if endpoint == "" { return nil } - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := rpc.NewServer() - for _, api := range apis { - if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return err - } - n.log.Debug("HTTP registered", "service", api.Service, "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp", endpoint); err != nil { + listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts) + if err != nil { return err } - go rpc.NewHTTPServer(cors, vhosts, handler).Serve(listener) n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) // All listeners booted successfully n.httpEndpoint = endpoint @@ -423,32 +379,11 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig if endpoint == "" { return nil } - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := rpc.NewServer() - for _, api := range apis { - if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return err - } - n.log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp", endpoint); err != nil { + listener, handler, err := rpc.StartWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) + if err != nil { return err } - go rpc.NewWSServer(wsOrigins, handler).Serve(listener) n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) - // All listeners booted successfully n.wsEndpoint = endpoint n.wsListener = listener diff --git a/rpc/client.go b/rpc/client.go index 8aa84ec982..68745c6cbe 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -33,6 +33,7 @@ import ( "time" "github.com/ethereum/go-ethereum/log" + "os" ) var ( @@ -171,6 +172,8 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) { return DialHTTP(rawurl) case "ws", "wss": return DialWebsocket(ctx, rawurl, "") + case "stdio": + return DialStdIO(ctx) case "": return DialIPC(ctx, rawurl) default: @@ -178,13 +181,51 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) { } } +type StdIOConn struct{} + +func (io StdIOConn) Read(b []byte) (n int, err error) { + return os.Stdin.Read(b) +} + +func (io StdIOConn) Write(b []byte) (n int, err error) { + return os.Stdout.Write(b) +} + +func (io StdIOConn) Close() error { + return nil +} + +func (io StdIOConn) LocalAddr() net.Addr { + return &net.UnixAddr{Name: "stdio", Net: "stdio"} +} + +func (io StdIOConn) RemoteAddr() net.Addr { + return &net.UnixAddr{Name: "stdio", Net: "stdio"} +} + +func (io StdIOConn) SetDeadline(t time.Time) error { + return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} +} + +func (io StdIOConn) SetReadDeadline(t time.Time) error { + return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} +} + +func (io StdIOConn) SetWriteDeadline(t time.Time) error { + return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} +} +func DialStdIO(ctx context.Context) (*Client, error) { + return newClient(ctx, func(_ context.Context) (net.Conn, error) { + return StdIOConn{}, nil + }) +} + func newClient(initctx context.Context, connectFunc func(context.Context) (net.Conn, error)) (*Client, error) { conn, err := connectFunc(initctx) if err != nil { return nil, err } _, isHTTP := conn.(*httpConn) - c := &Client{ writeConn: conn, isHTTP: isHTTP, @@ -524,13 +565,13 @@ func (c *Client) dispatch(conn net.Conn) { } case err := <-c.readErr: - log.Debug(fmt.Sprintf("<-readErr: %v", err)) + log.Debug("<-readErr", "err", err) c.closeRequestOps(err) conn.Close() reading = false case newconn := <-c.reconnected: - log.Debug(fmt.Sprintf("<-reconnected: (reading=%t) %v", reading, conn.RemoteAddr())) + log.Debug("<-reconnected", "reading", reading, "remote", conn.RemoteAddr()) if reading { // Wait for the previous read loop to exit. This is a rare case. conn.Close() @@ -587,7 +628,7 @@ func (c *Client) closeRequestOps(err error) { func (c *Client) handleNotification(msg *jsonrpcMessage) { if !strings.HasSuffix(msg.Method, notificationMethodSuffix) { - log.Debug(fmt.Sprint("dropping non-subscription message: ", msg)) + log.Debug("dropping non-subscription message", "msg", msg) return } var subResult struct { @@ -595,7 +636,7 @@ func (c *Client) handleNotification(msg *jsonrpcMessage) { Result json.RawMessage `json:"result"` } if err := json.Unmarshal(msg.Params, &subResult); err != nil { - log.Debug(fmt.Sprint("dropping invalid subscription message: ", msg)) + log.Debug("dropping invalid subscription message", "msg", msg) return } if c.subs[subResult.ID] != nil { @@ -606,7 +647,7 @@ func (c *Client) handleNotification(msg *jsonrpcMessage) { func (c *Client) handleResponse(msg *jsonrpcMessage) { op := c.respWait[string(msg.ID)] if op == nil { - log.Debug(fmt.Sprintf("unsolicited response %v", msg)) + log.Debug("unsolicited response", "msg", msg) return } delete(c.respWait, string(msg.ID)) diff --git a/rpc/endpoints.go b/rpc/endpoints.go new file mode 100644 index 0000000000..9ba2ed9707 --- /dev/null +++ b/rpc/endpoints.go @@ -0,0 +1,120 @@ +// Copyright 2018 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 . + +package rpc + +import ( + "github.com/ethereum/go-ethereum/log" + "net" +) + +// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules +func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string) (net.Listener, *Server, error) { + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := NewServer() + for _, api := range apis { + if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("HTTP registered", "namespace", api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, nil, err + } + go NewHTTPServer(cors, vhosts, handler).Serve(listener) + return listener, handler, err +} + +// StartWSEndpoint starts a websocket endpoint +func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { + + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := NewServer() + for _, api := range apis { + if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, nil, err + } + go NewWSServer(wsOrigins, handler).Serve(listener) + return listener, handler, err + +} + +// StartIPCEndpoint starts an IPC endpoint +func StartIPCEndpoint(isClosedFn func() bool, ipcEndpoint string, apis []API) (net.Listener, *Server, error) { + // Register all the APIs exposed by the services + handler := NewServer() + for _, api := range apis { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("IPC registered", "namespace", api.Namespace) + } + // All APIs registered, start the IPC listener + var ( + listener net.Listener + err error + ) + if listener, err = CreateIPCListener(ipcEndpoint); err != nil { + return nil, nil, err + } + go func() { + for { + conn, err := listener.Accept() + if err != nil { + // Terminate if the listener was closed + if isClosedFn() { + log.Info("IPC closed", "err", err) + } else { + // Not closed, just some error; report and continue + log.Error("IPC accept failed", "err", err) + } + continue + } + go handler.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions) + } + }() + + return listener, handler, nil +} diff --git a/rpc/http.go b/rpc/http.go index e8f51150f4..14b6c1ab42 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -169,12 +169,17 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // All checks passed, create a codec that reads direct from the request body // untilEOF and writes the response to w and order the server to process a // single request. + ctx := context.Background() + ctx = context.WithValue(ctx, "remote", r.RemoteAddr) + ctx = context.WithValue(ctx, "scheme", r.Proto) + ctx = context.WithValue(ctx, "local", r.Host) + body := io.LimitReader(r.Body, maxRequestContentLength) codec := NewJSONCodec(&httpReadWriteNopCloser{body, w}) defer codec.Close() w.Header().Set("content-type", contentType) - srv.ServeSingleRequest(codec, OptionMethodInvocation) + srv.ServeSingleRequest(codec, OptionMethodInvocation, ctx) } // validateRequest returns a non-zero response code and error message if the diff --git a/rpc/server.go b/rpc/server.go index 11373b504c..0f29035edb 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -125,7 +125,7 @@ func (s *Server) RegisterName(name string, rcvr interface{}) error { // If singleShot is true it will process a single request, otherwise it will handle // requests until the codec returns an error when reading a request (in most cases // an EOF). It executes requests in parallel when singleShot is false. -func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption) error { +func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption, ctx context.Context) error { var pend sync.WaitGroup defer func() { @@ -140,7 +140,8 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO s.codecsMu.Unlock() }() - ctx, cancel := context.WithCancel(context.Background()) + // ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(ctx) defer cancel() // if the codec supports notification include a notifier that callbacks can use @@ -215,14 +216,14 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO // stopped. In either case the codec is closed. func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) { defer codec.Close() - s.serveRequest(codec, false, options) + s.serveRequest(codec, false, options, context.Background()) } // ServeSingleRequest reads and processes a single RPC request from the given codec. It will not // close the codec unless a non-recoverable error has occurred. Note, this method will return after // a single request has been processed! -func (s *Server) ServeSingleRequest(codec ServerCodec, options CodecOption) { - s.serveRequest(codec, true, options) +func (s *Server) ServeSingleRequest(codec ServerCodec, options CodecOption, ctx context.Context) { + s.serveRequest(codec, true, options, ctx) } // Stop will stop reading new requests, wait for stopPendingRequestTimeout to allow pending requests to finish, diff --git a/signer/core/abihelper.go b/signer/core/abihelper.go new file mode 100644 index 0000000000..2674c7346a --- /dev/null +++ b/signer/core/abihelper.go @@ -0,0 +1,256 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + "bytes" + "os" + "regexp" +) + +type decodedArgument struct { + soltype abi.Argument + value interface{} +} +type decodedCallData struct { + signature string + name string + inputs []decodedArgument +} + +// String implements stringer interface, tries to use the underlying value-type +func (arg decodedArgument) String() string { + var value string + switch arg.value.(type) { + case fmt.Stringer: + value = arg.value.(fmt.Stringer).String() + default: + value = fmt.Sprintf("%v", arg.value) + } + return fmt.Sprintf("%v: %v", arg.soltype.Type.String(), value) +} + +// String implements stringer interface for decodedCallData +func (cd decodedCallData) String() string { + args := make([]string, len(cd.inputs)) + for i, arg := range cd.inputs { + args[i] = arg.String() + } + return fmt.Sprintf("%s(%s)", cd.name, strings.Join(args, ",")) +} + +// parseCallData matches the provided call data against the abi definition, +// and returns a struct containing the actual go-typed values +func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) { + + if len(calldata) < 4 { + return nil, fmt.Errorf("Invalid ABI-data, incomplete method signature of (%d bytes)", len(calldata)) + } + + sigdata, argdata := calldata[:4], calldata[4:] + if len(argdata)%32 != 0 { + return nil, fmt.Errorf("Not ABI-encoded data; length should be a multiple of 32 (was %d)", len(argdata)) + } + + abispec, err := abi.JSON(strings.NewReader(abidata)) + if err != nil { + return nil, fmt.Errorf("Failed parsing JSON ABI: %v, abidata: %v", err, abidata) + } + + method, err := abispec.MethodById(sigdata) + if err != nil { + return nil, err + } + + v, err := method.Inputs.UnpackValues(argdata) + if err != nil { + return nil, err + } + + decoded := decodedCallData{signature: method.Sig(), name: method.Name} + + for n, argument := range method.Inputs { + if err != nil { + return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err) + } else { + decodedArg := decodedArgument{ + soltype: argument, + value: v[n], + } + decoded.inputs = append(decoded.inputs, decodedArg) + } + } + + // We're finished decoding the data. At this point, we encode the decoded data to see if it matches with the + // original data. If we didn't do that, it would e.g. be possible to stuff extra data into the arguments, which + // is not detected by merely decoding the data. + + var ( + encoded []byte + ) + encoded, err = method.Inputs.PackValues(v) + + if err != nil { + return nil, err + } + + if !bytes.Equal(encoded, argdata) { + was := common.Bytes2Hex(encoded) + exp := common.Bytes2Hex(argdata) + return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig()) + } + return &decoded, nil +} + +// MethodSelectorToAbi converts a method selector into an ABI struct. The returned data is a valid json string +// which can be consumed by the standard abi package. +func MethodSelectorToAbi(selector string) ([]byte, error) { + + re := regexp.MustCompile(`^([^\)]+)\(([a-z0-9,\[\]]*)\)`) + + type fakeArg struct { + Type string `json:"type"` + } + type fakeABI struct { + Name string `json:"name"` + Type string `json:"type"` + Inputs []fakeArg `json:"inputs"` + } + groups := re.FindStringSubmatch(selector) + if len(groups) != 3 { + return nil, fmt.Errorf("Did not match: %v (%v matches)", selector, len(groups)) + } + name := groups[1] + args := groups[2] + arguments := make([]fakeArg, 0) + if len(args) > 0 { + for _, arg := range strings.Split(args, ",") { + arguments = append(arguments, fakeArg{arg}) + } + } + abicheat := fakeABI{ + name, "function", arguments, + } + return json.Marshal([]fakeABI{abicheat}) + +} + +type AbiDb struct { + db map[string]string + customdb map[string]string + customdbPath string +} + +// NewEmptyAbiDB exists for test purposes +func NewEmptyAbiDB() (*AbiDb, error) { + return &AbiDb{make(map[string]string), make(map[string]string), ""}, nil +} + +// NewAbiDBFromFile loads signature database from file, and +// errors if the file is not valid json. Does no other validation of contents +func NewAbiDBFromFile(path string) (*AbiDb, error) { + raw, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + db, err := NewEmptyAbiDB() + if err != nil { + return nil, err + } + json.Unmarshal(raw, &db.db) + return db, nil +} + +// NewAbiDBFromFiles loads both the standard signature database and a custom database. The latter will be used +// to write new values into if they are submitted via the API +func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) { + + db := &AbiDb{make(map[string]string), make(map[string]string), custom} + db.customdbPath = custom + + raw, err := ioutil.ReadFile(standard) + if err != nil { + return nil, err + } + json.Unmarshal(raw, &db.db) + // Custom file may not exist. Will be created during save, if needed + if _, err := os.Stat(custom); err == nil { + raw, err = ioutil.ReadFile(custom) + if err != nil { + return nil, err + } + json.Unmarshal(raw, &db.customdb) + } + + return db, nil +} + +// LookupMethodSelector checks the given 4byte-sequence against the known ABI methods. +// OBS: This method does not validate the match, it's assumed the caller will do so +func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) { + if len(id) < 4 { + return "", fmt.Errorf("Expected 4-byte id, got %d", len(id)) + } + sig := common.ToHex(id[:4]) + if key, exists := db.db[sig]; exists { + return key, nil + } + if key, exists := db.customdb[sig]; exists { + return key, nil + } + return "", fmt.Errorf("Signature %v not found", sig) +} +func (db *AbiDb) Size() int { + return len(db.db) +} + +// saveCustomAbi saves a signature ephemerally. If custom file is used, also saves to disk +func (db *AbiDb) saveCustomAbi(selector, signature string) error { + db.customdb[signature] = selector + if db.customdbPath == "" { + return nil //Not an error per se, just not used + } + d, err := json.Marshal(db.customdb) + if err != nil { + return err + } + err = ioutil.WriteFile(db.customdbPath, d, 0600) + return err +} + +// Adds a signature to the database, if custom database saving is enabled. +// OBS: This method does _not_ validate the correctness of the data, +// it is assumed that the caller has already done so +func (db *AbiDb) AddSignature(selector string, data []byte) error { + if len(data) < 4 { + return nil + } + _, err := db.LookupMethodSelector(data[:4]) + if err == nil { + return nil + } + sig := common.ToHex(data[:4]) + return db.saveCustomAbi(selector, sig) +} diff --git a/signer/core/abihelper_test.go b/signer/core/abihelper_test.go new file mode 100644 index 0000000000..8bb5776691 --- /dev/null +++ b/signer/core/abihelper_test.go @@ -0,0 +1,247 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "fmt" + "strings" + "testing" + + "io/ioutil" + "math/big" + "reflect" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" +) + +func verify(t *testing.T, jsondata, calldata string, exp []interface{}) { + + abispec, err := abi.JSON(strings.NewReader(jsondata)) + if err != nil { + t.Fatal(err) + } + cd := common.Hex2Bytes(calldata) + sigdata, argdata := cd[:4], cd[4:] + method, err := abispec.MethodById(sigdata) + + if err != nil { + t.Fatal(err) + } + + data, err := method.Inputs.UnpackValues(argdata) + + if len(data) != len(exp) { + t.Fatalf("Mismatched length, expected %d, got %d", len(exp), len(data)) + } + for i, elem := range data { + if !reflect.DeepEqual(elem, exp[i]) { + t.Fatalf("Unpack error, arg %d, got %v, want %v", i, elem, exp[i]) + } + } +} +func TestNewUnpacker(t *testing.T) { + type unpackTest struct { + jsondata string + calldata string + exp []interface{} + } + testcases := []unpackTest{ + { // https://solidity.readthedocs.io/en/develop/abi-spec.html#use-of-dynamic-types + `[{"type":"function","name":"f", "inputs":[{"type":"uint256"},{"type":"uint32[]"},{"type":"bytes10"},{"type":"bytes"}]}]`, + // 0x123, [0x456, 0x789], "1234567890", "Hello, world!" + "8be65246" + "00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000", + []interface{}{ + big.NewInt(0x123), + []uint32{0x456, 0x789}, + [10]byte{49, 50, 51, 52, 53, 54, 55, 56, 57, 48}, + common.Hex2Bytes("48656c6c6f2c20776f726c6421"), + }, + }, { // https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#examples + `[{"type":"function","name":"sam","inputs":[{"type":"bytes"},{"type":"bool"},{"type":"uint256[]"}]}]`, + // "dave", true and [1,2,3] + "a5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + []interface{}{ + []byte{0x64, 0x61, 0x76, 0x65}, + true, + []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}, + }, + }, { + `[{"type":"function","name":"send","inputs":[{"type":"uint256"}]}]`, + "a52c101e0000000000000000000000000000000000000000000000000000000000000012", + []interface{}{big.NewInt(0x12)}, + }, { + `[{"type":"function","name":"compareAndApprove","inputs":[{"type":"address"},{"type":"uint256"},{"type":"uint256"}]}]`, + "751e107900000000000000000000000000000133700000deadbeef00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + []interface{}{ + common.HexToAddress("0x00000133700000deadbeef000000000000000000"), + new(big.Int).SetBytes([]byte{0x00}), + big.NewInt(0x1), + }, + }, + } + for _, c := range testcases { + verify(t, c.jsondata, c.calldata, c.exp) + } + +} + +/* +func TestReflect(t *testing.T) { + a := big.NewInt(0) + b := new(big.Int).SetBytes([]byte{0x00}) + if !reflect.DeepEqual(a, b) { + t.Fatalf("Nope, %v != %v", a, b) + } +} +*/ + +func TestCalldataDecoding(t *testing.T) { + + // send(uint256) : a52c101e + // compareAndApprove(address,uint256,uint256) : 751e1079 + // issue(address[],uint256) : 42958b54 + jsondata := ` +[ + {"type":"function","name":"send","inputs":[{"name":"a","type":"uint256"}]}, + {"type":"function","name":"compareAndApprove","inputs":[{"name":"a","type":"address"},{"name":"a","type":"uint256"},{"name":"a","type":"uint256"}]}, + {"type":"function","name":"issue","inputs":[{"name":"a","type":"address[]"},{"name":"a","type":"uint256"}]}, + {"type":"function","name":"sam","inputs":[{"name":"a","type":"bytes"},{"name":"a","type":"bool"},{"name":"a","type":"uint256[]"}]} +]` + //Expected failures + for _, hexdata := range []string{ + "a52c101e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042", + "a52c101e000000000000000000000000000000000000000000000000000000000000001200", + "a52c101e00000000000000000000000000000000000000000000000000000000000000", + "a52c101e", + "a52c10", + "", + // Too short + "751e10790000000000000000000000000000000000000000000000000000000000000012", + "751e1079FFffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + //Not valid multiple of 32 + "deadbeef00000000000000000000000000000000000000000000000000000000000000", + //Too short 'issue' + "42958b5400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042", + // Too short compareAndApprove + "a52c101e00ff0000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042", + // From https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI + // contains a bool with illegal values + "a5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + } { + _, err := parseCallData(common.Hex2Bytes(hexdata), jsondata) + if err == nil { + t.Errorf("Expected decoding to fail: %s", hexdata) + } + } + + //Expected success + for _, hexdata := range []string{ + // From https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI + "a5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "a52c101e0000000000000000000000000000000000000000000000000000000000000012", + "a52c101eFFffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "751e1079000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "42958b54" + + // start of dynamic type + "0000000000000000000000000000000000000000000000000000000000000040" + + //uint256 + "0000000000000000000000000000000000000000000000000000000000000001" + + // length of array + "0000000000000000000000000000000000000000000000000000000000000002" + + // array values + "000000000000000000000000000000000000000000000000000000000000dead" + + "000000000000000000000000000000000000000000000000000000000000beef", + } { + _, err := parseCallData(common.Hex2Bytes(hexdata), jsondata) + if err != nil { + t.Errorf("Unexpected failure on input %s:\n %v (%d bytes) ", hexdata, err, len(common.Hex2Bytes(hexdata))) + } + } +} + +func TestSelectorUnmarshalling(t *testing.T) { + var ( + db *AbiDb + err error + abistring []byte + abistruct abi.ABI + ) + + db, err = NewAbiDBFromFile("../../cmd/clef/4byte.json") + if err != nil { + t.Fatal(err) + } + fmt.Printf("DB size %v\n", db.Size()) + for id, selector := range db.db { + + abistring, err = MethodSelectorToAbi(selector) + if err != nil { + t.Error(err) + return + } + abistruct, err = abi.JSON(strings.NewReader(string(abistring))) + if err != nil { + t.Error(err) + return + } + m, err := abistruct.MethodById(common.Hex2Bytes(id[2:])) + if err != nil { + t.Error(err) + return + } + if m.Sig() != selector { + t.Errorf("Expected equality: %v != %v", m.Sig(), selector) + } + } + +} + +func TestCustomABI(t *testing.T) { + d, err := ioutil.TempDir("", "signer-4byte-test") + if err != nil { + t.Fatal(err) + } + filename := fmt.Sprintf("%s/4byte_custom.json", d) + abidb, err := NewAbiDBFromFiles("../../cmd/clef/4byte.json", filename) + if err != nil { + t.Fatal(err) + } + // Now we'll remove all existing signatures + abidb.db = make(map[string]string) + calldata := common.Hex2Bytes("a52c101edeadbeef") + _, err = abidb.LookupMethodSelector(calldata) + if err == nil { + t.Fatalf("Should not find a match on empty db") + } + if err = abidb.AddSignature("send(uint256)", calldata); err != nil { + t.Fatalf("Failed to save file: %v", err) + } + _, err = abidb.LookupMethodSelector(calldata) + if err != nil { + t.Fatalf("Should find a match for abi signature, got: %v", err) + } + //Check that it wrote to file + abidb2, err := NewAbiDBFromFile(filename) + if err != nil { + t.Fatalf("Failed to create new abidb: %v", err) + } + _, err = abidb2.LookupMethodSelector(calldata) + if err != nil { + t.Fatalf("Save failed: should find a match for abi signature after loading from disk") + } +} diff --git a/signer/core/api.go b/signer/core/api.go new file mode 100644 index 0000000000..1387041cc3 --- /dev/null +++ b/signer/core/api.go @@ -0,0 +1,500 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "math/big" + "reflect" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/accounts/usbwallet" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +// ExternalAPI defines the external API through which signing requests are made. +type ExternalAPI interface { + // List available accounts + List(ctx context.Context) (Accounts, error) + // New request to create a new account + New(ctx context.Context) (accounts.Account, error) + // SignTransaction request to sign the specified transaction + SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) + // Sign - request to sign the given data (plus prefix) + Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) + // EcRecover - request to perform ecrecover + EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) + // Export - request to export an account + Export(ctx context.Context, addr common.Address) (json.RawMessage, error) + // Import - request to import an account + Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) +} + +// SignerUI specifies what method a UI needs to implement to be able to be used as a UI for the signer +type SignerUI interface { + // ApproveTx prompt the user for confirmation to request to sign Transaction + ApproveTx(request *SignTxRequest) (SignTxResponse, error) + // ApproveSignData prompt the user for confirmation to request to sign data + ApproveSignData(request *SignDataRequest) (SignDataResponse, error) + // ApproveExport prompt the user for confirmation to export encrypted Account json + ApproveExport(request *ExportRequest) (ExportResponse, error) + // ApproveImport prompt the user for confirmation to import Account json + ApproveImport(request *ImportRequest) (ImportResponse, error) + // ApproveListing prompt the user for confirmation to list accounts + // the list of accounts to list can be modified by the UI + ApproveListing(request *ListRequest) (ListResponse, error) + // ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller + ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) + // ShowError displays error message to user + ShowError(message string) + // ShowInfo displays info message to user + ShowInfo(message string) + // OnApprovedTx notifies the UI about a transaction having been successfully signed. + // This method can be used by a UI to keep track of e.g. how much has been sent to a particular recipient. + OnApprovedTx(tx ethapi.SignTransactionResult) + // OnSignerStartup is invoked when the signer boots, and tells the UI info about external API location and version + // information + OnSignerStartup(info StartupInfo) +} + +// SignerAPI defines the actual implementation of ExternalAPI +type SignerAPI struct { + chainID *big.Int + am *accounts.Manager + UI SignerUI + validator *Validator +} + +// Metadata about a request +type Metadata struct { + Remote string `json:"remote"` + Local string `json:"local"` + Scheme string `json:"scheme"` +} + +// MetadataFromContext extracts Metadata from a given context.Context +func MetadataFromContext(ctx context.Context) Metadata { + m := Metadata{"NA", "NA", "NA"} // batman + + if v := ctx.Value("remote"); v != nil { + m.Remote = v.(string) + } + if v := ctx.Value("scheme"); v != nil { + m.Scheme = v.(string) + } + if v := ctx.Value("local"); v != nil { + m.Local = v.(string) + } + return m +} + +// String implements Stringer interface +func (m Metadata) String() string { + s, err := json.Marshal(m) + if err == nil { + return string(s) + } + return err.Error() +} + +// types for the requests/response types between signer and UI +type ( + // SignTxRequest contains info about a Transaction to sign + SignTxRequest struct { + Transaction SendTxArgs `json:"transaction"` + Callinfo []ValidationInfo `json:"call_info"` + Meta Metadata `json:"meta"` + } + // SignTxResponse result from SignTxRequest + SignTxResponse struct { + //The UI may make changes to the TX + Transaction SendTxArgs `json:"transaction"` + Approved bool `json:"approved"` + Password string `json:"password"` + } + // ExportRequest info about query to export accounts + ExportRequest struct { + Address common.Address `json:"address"` + Meta Metadata `json:"meta"` + } + // ExportResponse response to export-request + ExportResponse struct { + Approved bool `json:"approved"` + } + // ImportRequest info about request to import an Account + ImportRequest struct { + Meta Metadata `json:"meta"` + } + ImportResponse struct { + Approved bool `json:"approved"` + OldPassword string `json:"old_password"` + NewPassword string `json:"new_password"` + } + SignDataRequest struct { + Address common.MixedcaseAddress `json:"address"` + Rawdata hexutil.Bytes `json:"raw_data"` + Message string `json:"message"` + Hash hexutil.Bytes `json:"hash"` + Meta Metadata `json:"meta"` + } + SignDataResponse struct { + Approved bool `json:"approved"` + Password string + } + NewAccountRequest struct { + Meta Metadata `json:"meta"` + } + NewAccountResponse struct { + Approved bool `json:"approved"` + Password string `json:"password"` + } + ListRequest struct { + Accounts []Account `json:"accounts"` + Meta Metadata `json:"meta"` + } + ListResponse struct { + Accounts []Account `json:"accounts"` + } + Message struct { + Text string `json:"text"` + } + StartupInfo struct { + Info map[string]interface{} `json:"info"` + } +) + +var ErrRequestDenied = errors.New("Request denied") + +type errorWrapper struct { + msg string + err error +} + +func (ew errorWrapper) String() string { + return fmt.Sprintf("%s\n%s", ew.msg, ew.err) +} + +// NewSignerAPI creates a new API that can be used for Account management. +// ksLocation specifies the directory where to store the password protected private +// key that is generated when a new Account is created. +// noUSB disables USB support that is required to support hardware devices such as +// ledger and trezor. +func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *AbiDb, lightKDF bool) *SignerAPI { + var ( + backends []accounts.Backend + n, p = keystore.StandardScryptN, keystore.StandardScryptP + ) + if lightKDF { + n, p = keystore.LightScryptN, keystore.LightScryptP + } + // support password based accounts + if len(ksLocation) > 0 { + backends = append(backends, keystore.NewKeyStore(ksLocation, n, p)) + } + if !noUSB { + // Start a USB hub for Ledger hardware wallets + if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { + log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err)) + } else { + backends = append(backends, ledgerhub) + log.Debug("Ledger support enabled") + } + // Start a USB hub for Trezor hardware wallets + if trezorhub, err := usbwallet.NewTrezorHub(); err != nil { + log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) + } else { + backends = append(backends, trezorhub) + log.Debug("Trezor support enabled") + } + } + return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, NewValidator(abidb)} +} + +// List returns the set of wallet this signer manages. Each wallet can contain +// multiple accounts. +func (api *SignerAPI) List(ctx context.Context) (Accounts, error) { + var accs []Account + for _, wallet := range api.am.Wallets() { + for _, acc := range wallet.Accounts() { + acc := Account{Typ: "Account", URL: wallet.URL(), Address: acc.Address} + accs = append(accs, acc) + } + } + result, err := api.UI.ApproveListing(&ListRequest{Accounts: accs, Meta: MetadataFromContext(ctx)}) + if err != nil { + return nil, err + } + if result.Accounts == nil { + return nil, ErrRequestDenied + + } + return result.Accounts, nil +} + +// New creates a new password protected Account. The private key is protected with +// the given password. Users are responsible to backup the private key that is stored +// in the keystore location thas was specified when this API was created. +func (api *SignerAPI) New(ctx context.Context) (accounts.Account, error) { + be := api.am.Backends(keystore.KeyStoreType) + if len(be) == 0 { + return accounts.Account{}, errors.New("password based accounts not supported") + } + resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}) + + if err != nil { + return accounts.Account{}, err + } + if !resp.Approved { + return accounts.Account{}, ErrRequestDenied + } + return be[0].(*keystore.KeyStore).NewAccount(resp.Password) +} + +// logDiff logs the difference between the incoming (original) transaction and the one returned from the signer. +// it also returns 'true' if the transaction was modified, to make it possible to configure the signer not to allow +// UI-modifications to requests +func logDiff(original *SignTxRequest, new *SignTxResponse) bool { + modified := false + if f0, f1 := original.Transaction.From, new.Transaction.From; !reflect.DeepEqual(f0, f1) { + log.Info("Sender-account changed by UI", "was", f0, "is", f1) + modified = true + } + if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) { + log.Info("Recipient-account changed by UI", "was", t0, "is", t1) + modified = true + } + if g0, g1 := original.Transaction.Gas, new.Transaction.Gas; g0 != g1 { + modified = true + log.Info("Gas changed by UI", "was", g0, "is", g1) + } + if g0, g1 := big.Int(original.Transaction.GasPrice), big.Int(new.Transaction.GasPrice); g0.Cmp(&g1) != 0 { + modified = true + log.Info("GasPrice changed by UI", "was", g0, "is", g1) + } + if v0, v1 := big.Int(original.Transaction.Value), big.Int(new.Transaction.Value); v0.Cmp(&v1) != 0 { + modified = true + log.Info("Value changed by UI", "was", v0, "is", v1) + } + if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 { + d0s := "" + d1s := "" + if d0 != nil { + d0s = common.ToHex(*d0) + } + if d1 != nil { + d1s = common.ToHex(*d1) + } + if d1s != d0s { + modified = true + log.Info("Data changed by UI", "was", d0s, "is", d1s) + } + } + if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 { + modified = true + log.Info("Nonce changed by UI", "was", n0, "is", n1) + } + return modified +} + +// SignTransaction signs the given Transaction and returns it both as json and rlp-encoded form +func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) { + var ( + err error + result SignTxResponse + ) + msgs, err := api.validator.ValidateTransaction(&args, methodSelector) + if err != nil { + return nil, err + } + + req := SignTxRequest{ + Transaction: args, + Meta: MetadataFromContext(ctx), + Callinfo: msgs.Messages, + } + // Process approval + result, err = api.UI.ApproveTx(&req) + if err != nil { + return nil, err + } + if !result.Approved { + return nil, ErrRequestDenied + } + // Log changes made by the UI to the signing-request + logDiff(&req, &result) + var ( + acc accounts.Account + wallet accounts.Wallet + ) + acc = accounts.Account{Address: result.Transaction.From.Address()} + wallet, err = api.am.Find(acc) + if err != nil { + return nil, err + } + // Convert fields into a real transaction + var unsignedTx = result.Transaction.toTransaction() + + // The one to sign is the one that was returned from the UI + signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID) + if err != nil { + api.UI.ShowError(err.Error()) + return nil, err + } + + rlpdata, err := rlp.EncodeToBytes(signedTx) + response := ethapi.SignTransactionResult{Raw: rlpdata, Tx: signedTx} + + // Finally, send the signed tx to the UI + api.UI.OnApprovedTx(response) + // ...and to the external caller + return &response, nil + +} + +// Sign calculates an Ethereum ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)) +// +// Note, the produced signature conforms to the secp256k1 curve R, S and V values, +// where the V value will be 27 or 28 for legacy reasons. +// +// The key used to calculate the signature is decrypted with the given password. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign +func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) { + sighash, msg := SignHash(data) + // We make the request prior to looking up if we actually have the account, to prevent + // account-enumeration via the API + req := &SignDataRequest{Address: addr, Rawdata: data, Message: msg, Hash: sighash, Meta: MetadataFromContext(ctx)} + res, err := api.UI.ApproveSignData(req) + + if err != nil { + return nil, err + } + if !res.Approved { + return nil, ErrRequestDenied + } + // Look up the wallet containing the requested signer + account := accounts.Account{Address: addr.Address()} + wallet, err := api.am.Find(account) + if err != nil { + return nil, err + } + // Assemble sign the data with the wallet + signature, err := wallet.SignHashWithPassphrase(account, res.Password, sighash) + if err != nil { + api.UI.ShowError(err.Error()) + return nil, err + } + signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + return signature, nil +} + +// EcRecover returns the address for the Account that was used to create the signature. +// Note, this function is compatible with eth_sign and personal_sign. As such it recovers +// the address of: +// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) +// addr = ecrecover(hash, signature) +// +// Note, the signature must conform to the secp256k1 curve R, S and V values, where +// the V value must be be 27 or 28 for legacy reasons. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover +func (api *SignerAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { + if len(sig) != 65 { + return common.Address{}, fmt.Errorf("signature must be 65 bytes long") + } + if sig[64] != 27 && sig[64] != 28 { + return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") + } + sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 + hash, _ := SignHash(data) + rpk, err := crypto.Ecrecover(hash, sig) + if err != nil { + return common.Address{}, err + } + pubKey := crypto.ToECDSAPub(rpk) + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + return recoveredAddr, nil +} + +// SignHash is a helper function that calculates a hash for the given message that can be +// safely used to calculate a signature from. +// +// The hash is calculated as +// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). +// +// This gives context to the signed message and prevents signing of transactions. +func SignHash(data []byte) ([]byte, string) { + msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) + return crypto.Keccak256([]byte(msg)), msg +} + +// Export returns encrypted private key associated with the given address in web3 keystore format. +func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { + res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)}) + + if err != nil { + return nil, err + } + if !res.Approved { + return nil, ErrRequestDenied + } + // Look up the wallet containing the requested signer + wallet, err := api.am.Find(accounts.Account{Address: addr}) + if err != nil { + return nil, err + } + if wallet.URL().Scheme != keystore.KeyStoreScheme { + return nil, fmt.Errorf("Account is not a keystore-account") + } + return ioutil.ReadFile(wallet.URL().Path) +} + +// Imports tries to import the given keyJSON in the local keystore. The keyJSON data is expected to be +// in web3 keystore format. It will decrypt the keyJSON with the given passphrase and on successful +// decryption it will encrypt the key with the given newPassphrase and store it in the keystore. +func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) { + be := api.am.Backends(keystore.KeyStoreType) + + if len(be) == 0 { + return Account{}, errors.New("password based accounts not supported") + } + res, err := api.UI.ApproveImport(&ImportRequest{Meta: MetadataFromContext(ctx)}) + + if err != nil { + return Account{}, err + } + if !res.Approved { + return Account{}, ErrRequestDenied + } + acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, res.OldPassword, res.NewPassword) + if err != nil { + api.UI.ShowError(err.Error()) + return Account{}, err + } + return Account{Typ: "Account", URL: acc.URL, Address: acc.Address}, nil +} diff --git a/signer/core/api_test.go b/signer/core/api_test.go new file mode 100644 index 0000000000..50ad021987 --- /dev/null +++ b/signer/core/api_test.go @@ -0,0 +1,386 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . +// +package core + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/rlp" +) + +//Used for testing +type HeadlessUI struct { + controller chan string +} + +func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) { +} + +func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) { + fmt.Printf("OnApproved called") +} + +func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { + + switch <-ui.controller { + case "Y": + return SignTxResponse{request.Transaction, true, <-ui.controller}, nil + case "M": //Modify + old := big.Int(request.Transaction.Value) + newVal := big.NewInt(0).Add(&old, big.NewInt(1)) + request.Transaction.Value = hexutil.Big(*newVal) + return SignTxResponse{request.Transaction, true, <-ui.controller}, nil + default: + return SignTxResponse{request.Transaction, false, ""}, nil + } +} +func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { + if "Y" == <-ui.controller { + return SignDataResponse{true, <-ui.controller}, nil + } + return SignDataResponse{false, ""}, nil +} +func (ui *HeadlessUI) ApproveExport(request *ExportRequest) (ExportResponse, error) { + + return ExportResponse{<-ui.controller == "Y"}, nil + +} +func (ui *HeadlessUI) ApproveImport(request *ImportRequest) (ImportResponse, error) { + + if "Y" == <-ui.controller { + return ImportResponse{true, <-ui.controller, <-ui.controller}, nil + } + return ImportResponse{false, "", ""}, nil +} +func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) { + + switch <-ui.controller { + case "A": + return ListResponse{request.Accounts}, nil + case "1": + l := make([]Account, 1) + l[0] = request.Accounts[1] + return ListResponse{l}, nil + default: + return ListResponse{nil}, nil + } +} +func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { + + if "Y" == <-ui.controller { + return NewAccountResponse{true, <-ui.controller}, nil + } + return NewAccountResponse{false, ""}, nil +} +func (ui *HeadlessUI) ShowError(message string) { + //stdout is used by communication + fmt.Fprint(os.Stderr, message) +} +func (ui *HeadlessUI) ShowInfo(message string) { + //stdout is used by communication + fmt.Fprint(os.Stderr, message) +} + +func tmpDirName(t *testing.T) string { + d, err := ioutil.TempDir("", "eth-keystore-test") + if err != nil { + t.Fatal(err) + } + d, err = filepath.EvalSymlinks(d) + if err != nil { + t.Fatal(err) + } + return d +} + +func setup(t *testing.T) (*SignerAPI, chan string) { + + controller := make(chan string, 10) + + db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json") + if err != nil { + utils.Fatalf(err.Error()) + } + var ( + ui = &HeadlessUI{controller} + api = NewSignerAPI( + 1, + tmpDirName(t), + true, + ui, + db, + true) + ) + return api, controller +} +func createAccount(control chan string, api *SignerAPI, t *testing.T) { + + control <- "Y" + control <- "apassword" + _, err := api.New(context.Background()) + if err != nil { + t.Fatal(err) + } + // Some time to allow changes to propagate + time.Sleep(250 * time.Millisecond) +} +func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) { + control <- "N" + acc, err := api.New(context.Background()) + if err != ErrRequestDenied { + t.Fatal(err) + } + if acc.Address != (common.Address{}) { + t.Fatal("Empty address should be returned") + } +} +func list(control chan string, api *SignerAPI, t *testing.T) []Account { + control <- "A" + list, err := api.List(context.Background()) + if err != nil { + t.Fatal(err) + } + return list +} + +func TestNewAcc(t *testing.T) { + + api, control := setup(t) + verifyNum := func(num int) { + if list := list(control, api, t); len(list) != num { + t.Errorf("Expected %d accounts, got %d", num, len(list)) + } + } + // Testing create and create-deny + createAccount(control, api, t) + createAccount(control, api, t) + failCreateAccount(control, api, t) + failCreateAccount(control, api, t) + createAccount(control, api, t) + failCreateAccount(control, api, t) + createAccount(control, api, t) + failCreateAccount(control, api, t) + verifyNum(4) + + // Testing listing: + // Listing one Account + control <- "1" + list, err := api.List(context.Background()) + if err != nil { + t.Fatal(err) + } + if len(list) != 1 { + t.Fatalf("List should only show one Account") + } + // Listing denied + control <- "Nope" + list, err = api.List(context.Background()) + if len(list) != 0 { + t.Fatalf("List should be empty") + } + if err != ErrRequestDenied { + t.Fatal("Expected deny") + } +} + +func TestSignData(t *testing.T) { + + api, control := setup(t) + //Create two accounts + createAccount(control, api, t) + createAccount(control, api, t) + control <- "1" + list, err := api.List(context.Background()) + if err != nil { + t.Fatal(err) + } + a := common.NewMixedcaseAddress(list[0].Address) + + control <- "Y" + control <- "wrongpassword" + h, err := api.Sign(context.Background(), a, []byte("EHLO world")) + if h != nil { + t.Errorf("Expected nil-data, got %x", h) + } + if err != keystore.ErrDecrypt { + t.Errorf("Expected ErrLocked! %v", err) + } + + control <- "No way" + h, err = api.Sign(context.Background(), a, []byte("EHLO world")) + if h != nil { + t.Errorf("Expected nil-data, got %x", h) + } + if err != ErrRequestDenied { + t.Errorf("Expected ErrRequestDenied! %v", err) + } + + control <- "Y" + control <- "apassword" + h, err = api.Sign(context.Background(), a, []byte("EHLO world")) + + if err != nil { + t.Fatal(err) + } + if h == nil || len(h) != 65 { + t.Errorf("Expected 65 byte signature (got %d bytes)", len(h)) + } +} +func mkTestTx(from common.MixedcaseAddress) SendTxArgs { + to := common.NewMixedcaseAddress(common.HexToAddress("0x1337")) + gas := hexutil.Uint64(21000) + gasPrice := (hexutil.Big)(*big.NewInt(2000000000)) + value := (hexutil.Big)(*big.NewInt(1e18)) + nonce := (hexutil.Uint64)(0) + data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a")) + tx := SendTxArgs{ + From: from, + To: &to, + Gas: gas, + GasPrice: gasPrice, + Value: value, + Data: &data, + Nonce: nonce} + return tx +} + +func TestSignTx(t *testing.T) { + + var ( + list Accounts + res, res2 *ethapi.SignTransactionResult + err error + ) + + api, control := setup(t) + createAccount(control, api, t) + control <- "A" + list, err = api.List(context.Background()) + if err != nil { + t.Fatal(err) + } + a := common.NewMixedcaseAddress(list[0].Address) + + methodSig := "test(uint)" + tx := mkTestTx(a) + + control <- "Y" + control <- "wrongpassword" + res, err = api.SignTransaction(context.Background(), tx, &methodSig) + if res != nil { + t.Errorf("Expected nil-response, got %v", res) + } + if err != keystore.ErrDecrypt { + t.Errorf("Expected ErrLocked! %v", err) + } + + control <- "No way" + res, err = api.SignTransaction(context.Background(), tx, &methodSig) + if res != nil { + t.Errorf("Expected nil-response, got %v", res) + } + if err != ErrRequestDenied { + t.Errorf("Expected ErrRequestDenied! %v", err) + } + + control <- "Y" + control <- "apassword" + res, err = api.SignTransaction(context.Background(), tx, &methodSig) + + if err != nil { + t.Fatal(err) + } + parsedTx := &types.Transaction{} + rlp.Decode(bytes.NewReader(res.Raw), parsedTx) + //The tx should NOT be modified by the UI + if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 { + t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value()) + } + control <- "Y" + control <- "apassword" + + res2, err = api.SignTransaction(context.Background(), tx, &methodSig) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(res.Raw, res2.Raw) { + t.Error("Expected tx to be unmodified by UI") + } + + //The tx is modified by the UI + control <- "M" + control <- "apassword" + + res2, err = api.SignTransaction(context.Background(), tx, &methodSig) + if err != nil { + t.Fatal(err) + } + + parsedTx2 := &types.Transaction{} + rlp.Decode(bytes.NewReader(res.Raw), parsedTx2) + //The tx should be modified by the UI + if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 { + t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value()) + } + + if bytes.Equal(res.Raw, res2.Raw) { + t.Error("Expected tx to be modified by UI") + } + +} + +/* +func TestAsyncronousResponses(t *testing.T){ + + //Set up one account + api, control := setup(t) + createAccount(control, api, t) + + // Two transactions, the second one with larger value than the first + tx1 := mkTestTx() + newVal := big.NewInt(0).Add((*big.Int) (tx1.Value), big.NewInt(1)) + tx2 := mkTestTx() + tx2.Value = (*hexutil.Big)(newVal) + + control <- "W" //wait + control <- "Y" // + control <- "apassword" + control <- "Y" // + control <- "apassword" + + var err error + + h1, err := api.SignTransaction(context.Background(), common.HexToAddress("1111"), tx1, nil) + h2, err := api.SignTransaction(context.Background(), common.HexToAddress("2222"), tx2, nil) + + + } +*/ diff --git a/signer/core/auditlog.go b/signer/core/auditlog.go new file mode 100644 index 0000000000..d0ba733d2f --- /dev/null +++ b/signer/core/auditlog.go @@ -0,0 +1,110 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "context" + + "encoding/json" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" +) + +type AuditLogger struct { + log log.Logger + api ExternalAPI +} + +func (l *AuditLogger) List(ctx context.Context) (Accounts, error) { + l.log.Info("List", "type", "request", "metadata", MetadataFromContext(ctx).String()) + res, e := l.api.List(ctx) + + l.log.Info("List", "type", "response", "data", res.String()) + + return res, e +} + +func (l *AuditLogger) New(ctx context.Context) (accounts.Account, error) { + return l.api.New(ctx) +} + +func (l *AuditLogger) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) { + sel := "" + if methodSelector != nil { + sel = *methodSelector + } + l.log.Info("SignTransaction", "type", "request", "metadata", MetadataFromContext(ctx).String(), + "tx", args.String(), + "methodSelector", sel) + + res, e := l.api.SignTransaction(ctx, args, methodSelector) + if res != nil { + l.log.Info("SignTransaction", "type", "response", "data", common.Bytes2Hex(res.Raw), "error", e) + } else { + l.log.Info("SignTransaction", "type", "response", "data", res, "error", e) + } + return res, e +} + +func (l *AuditLogger) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) { + l.log.Info("Sign", "type", "request", "metadata", MetadataFromContext(ctx).String(), + "addr", addr.String(), "data", common.Bytes2Hex(data)) + b, e := l.api.Sign(ctx, addr, data) + l.log.Info("Sign", "type", "response", "data", common.Bytes2Hex(b), "error", e) + return b, e +} + +func (l *AuditLogger) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { + l.log.Info("EcRecover", "type", "request", "metadata", MetadataFromContext(ctx).String(), + "data", common.Bytes2Hex(data)) + a, e := l.api.EcRecover(ctx, data, sig) + l.log.Info("EcRecover", "type", "response", "addr", a.String(), "error", e) + return a, e +} + +func (l *AuditLogger) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { + l.log.Info("Export", "type", "request", "metadata", MetadataFromContext(ctx).String(), + "addr", addr.Hex()) + j, e := l.api.Export(ctx, addr) + // In this case, we don't actually log the json-response, which may be extra sensitive + l.log.Info("Export", "type", "response", "json response size", len(j), "error", e) + return j, e +} + +func (l *AuditLogger) Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) { + // Don't actually log the json contents + l.log.Info("Import", "type", "request", "metadata", MetadataFromContext(ctx).String(), + "keyJSON size", len(keyJSON)) + a, e := l.api.Import(ctx, keyJSON) + l.log.Info("Import", "type", "response", "addr", a.String(), "error", e) + return a, e +} + +func NewAuditLogger(path string, api ExternalAPI) (*AuditLogger, error) { + l := log.New("api", "signer") + handler, err := log.FileHandler(path, log.LogfmtFormat()) + if err != nil { + return nil, err + } + l.SetHandler(handler) + l.Info("Configured", "audit log", path) + return &AuditLogger{l, api}, nil +} diff --git a/signer/core/cliui.go b/signer/core/cliui.go new file mode 100644 index 0000000000..0d9b5f3d36 --- /dev/null +++ b/signer/core/cliui.go @@ -0,0 +1,247 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . +package core + +import ( + "bufio" + "fmt" + "os" + "strings" + + "sync" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" + "golang.org/x/crypto/ssh/terminal" +) + +type CommandlineUI struct { + in *bufio.Reader + mu sync.Mutex +} + +func NewCommandlineUI() *CommandlineUI { + return &CommandlineUI{in: bufio.NewReader(os.Stdin)} +} + +// readString reads a single line from stdin, trimming if from spaces, enforcing +// non-emptyness. +func (ui *CommandlineUI) readString() string { + for { + fmt.Printf("> ") + text, err := ui.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text != "" { + return text + } + } +} + +// readPassword reads a single line from stdin, trimming it from the trailing new +// line and returns it. The input will not be echoed. +func (ui *CommandlineUI) readPassword() string { + fmt.Printf("Enter password to approve:\n") + fmt.Printf("> ") + + text, err := terminal.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + log.Crit("Failed to read password", "err", err) + } + fmt.Println() + fmt.Println("-----------------------") + return string(text) +} + +// readPassword reads a single line from stdin, trimming it from the trailing new +// line and returns it. The input will not be echoed. +func (ui *CommandlineUI) readPasswordText(inputstring string) string { + fmt.Printf("Enter %s:\n", inputstring) + fmt.Printf("> ") + text, err := terminal.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + log.Crit("Failed to read password", "err", err) + } + fmt.Println("-----------------------") + return string(text) +} + +// confirm returns true if user enters 'Yes', otherwise false +func (ui *CommandlineUI) confirm() bool { + fmt.Printf("Approve? [y/N]:\n") + if ui.readString() == "y" { + return true + } + fmt.Println("-----------------------") + return false +} + +func showMetadata(metadata Metadata) { + fmt.Printf("Request context:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local) +} + +// ApproveTx prompt the user for confirmation to request to sign Transaction +func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { + ui.mu.Lock() + defer ui.mu.Unlock() + weival := request.Transaction.Value.ToInt() + fmt.Printf("--------- Transaction request-------------\n") + if to := request.Transaction.To; to != nil { + fmt.Printf("to: %v\n", to.Original()) + if !to.ValidChecksum() { + fmt.Printf("\nWARNING: Invalid checksum on to-address!\n\n") + } + } else { + fmt.Printf("to: \n") + } + fmt.Printf("from: %v\n", request.Transaction.From.String()) + fmt.Printf("value: %v wei\n", weival) + if request.Transaction.Data != nil { + d := *request.Transaction.Data + if len(d) > 0 { + fmt.Printf("data: %v\n", common.Bytes2Hex(d)) + } + } + if request.Callinfo != nil { + fmt.Printf("\nTransaction validation:\n") + for _, m := range request.Callinfo { + fmt.Printf(" * %s : %s", m.Typ, m.Message) + } + fmt.Println() + + } + fmt.Printf("\n") + showMetadata(request.Meta) + fmt.Printf("-------------------------------------------\n") + if !ui.confirm() { + return SignTxResponse{request.Transaction, false, ""}, nil + } + return SignTxResponse{request.Transaction, true, ui.readPassword()}, nil +} + +// ApproveSignData prompt the user for confirmation to request to sign data +func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { + ui.mu.Lock() + defer ui.mu.Unlock() + + fmt.Printf("-------- Sign data request--------------\n") + fmt.Printf("Account: %s\n", request.Address.String()) + fmt.Printf("message: \n%q\n", request.Message) + fmt.Printf("raw data: \n%v\n", request.Rawdata) + fmt.Printf("message hash: %v\n", request.Hash) + fmt.Printf("-------------------------------------------\n") + showMetadata(request.Meta) + if !ui.confirm() { + return SignDataResponse{false, ""}, nil + } + return SignDataResponse{true, ui.readPassword()}, nil +} + +// ApproveExport prompt the user for confirmation to export encrypted Account json +func (ui *CommandlineUI) ApproveExport(request *ExportRequest) (ExportResponse, error) { + ui.mu.Lock() + defer ui.mu.Unlock() + + fmt.Printf("-------- Export Account request--------------\n") + fmt.Printf("A request has been made to export the (encrypted) keyfile\n") + fmt.Printf("Approving this operation means that the caller obtains the (encrypted) contents\n") + fmt.Printf("\n") + fmt.Printf("Account: %x\n", request.Address) + //fmt.Printf("keyfile: \n%v\n", request.file) + fmt.Printf("-------------------------------------------\n") + showMetadata(request.Meta) + return ExportResponse{ui.confirm()}, nil +} + +// ApproveImport prompt the user for confirmation to import Account json +func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse, error) { + ui.mu.Lock() + defer ui.mu.Unlock() + + fmt.Printf("-------- Import Account request--------------\n") + fmt.Printf("A request has been made to import an encrypted keyfile\n") + fmt.Printf("-------------------------------------------\n") + showMetadata(request.Meta) + if !ui.confirm() { + return ImportResponse{false, "", ""}, nil + } + return ImportResponse{true, ui.readPasswordText("Old password"), ui.readPasswordText("New password")}, nil +} + +// ApproveListing prompt the user for confirmation to list accounts +// the list of accounts to list can be modified by the UI +func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListResponse, error) { + + ui.mu.Lock() + defer ui.mu.Unlock() + + fmt.Printf("-------- List Account request--------------\n") + fmt.Printf("A request has been made to list all accounts. \n") + fmt.Printf("You can select which accounts the caller can see\n") + for _, account := range request.Accounts { + fmt.Printf("\t[x] %v\n", account.Address.Hex()) + } + fmt.Printf("-------------------------------------------\n") + showMetadata(request.Meta) + if !ui.confirm() { + return ListResponse{nil}, nil + } + return ListResponse{request.Accounts}, nil +} + +// ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller +func (ui *CommandlineUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { + + ui.mu.Lock() + defer ui.mu.Unlock() + + fmt.Printf("-------- New Account request--------------\n") + fmt.Printf("A request has been made to create a new. \n") + fmt.Printf("Approving this operation means that a new Account is created,\n") + fmt.Printf("and the address show to the caller\n") + showMetadata(request.Meta) + if !ui.confirm() { + return NewAccountResponse{false, ""}, nil + } + return NewAccountResponse{true, ui.readPassword()}, nil +} + +// ShowError displays error message to user +func (ui *CommandlineUI) ShowError(message string) { + + fmt.Printf("ERROR: %v\n", message) +} + +// ShowInfo displays info message to user +func (ui *CommandlineUI) ShowInfo(message string) { + fmt.Printf("Info: %v\n", message) +} + +func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) { + fmt.Printf("Transaction signed:\n ") + spew.Dump(tx.Tx) +} + +func (ui *CommandlineUI) OnSignerStartup(info StartupInfo) { + + fmt.Printf("------- Signer info -------\n") + for k, v := range info.Info { + fmt.Printf("* %v : %v\n", k, v) + } +} diff --git a/signer/core/stdioui.go b/signer/core/stdioui.go new file mode 100644 index 0000000000..5640ed03bd --- /dev/null +++ b/signer/core/stdioui.go @@ -0,0 +1,113 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . +// + +package core + +import ( + "context" + "sync" + + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" +) + +type StdIOUI struct { + client rpc.Client + mu sync.Mutex +} + +func NewStdIOUI() *StdIOUI { + log.Info("NewStdIOUI") + client, err := rpc.DialContext(context.Background(), "stdio://") + if err != nil { + log.Crit("Could not create stdio client", "err", err) + } + return &StdIOUI{client: *client} +} + +// dispatch sends a request over the stdio +func (ui *StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error { + err := ui.client.Call(&reply, serviceMethod, args) + if err != nil { + log.Info("Error", "exc", err.Error()) + } + return err +} + +func (ui *StdIOUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { + var result SignTxResponse + err := ui.dispatch("ApproveTx", request, &result) + return result, err +} + +func (ui *StdIOUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { + var result SignDataResponse + err := ui.dispatch("ApproveSignData", request, &result) + return result, err +} + +func (ui *StdIOUI) ApproveExport(request *ExportRequest) (ExportResponse, error) { + var result ExportResponse + err := ui.dispatch("ApproveExport", request, &result) + return result, err +} + +func (ui *StdIOUI) ApproveImport(request *ImportRequest) (ImportResponse, error) { + var result ImportResponse + err := ui.dispatch("ApproveImport", request, &result) + return result, err +} + +func (ui *StdIOUI) ApproveListing(request *ListRequest) (ListResponse, error) { + var result ListResponse + err := ui.dispatch("ApproveListing", request, &result) + return result, err +} + +func (ui *StdIOUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { + var result NewAccountResponse + err := ui.dispatch("ApproveNewAccount", request, &result) + return result, err +} + +func (ui *StdIOUI) ShowError(message string) { + err := ui.dispatch("ShowError", &Message{message}, nil) + if err != nil { + log.Info("Error calling 'ShowError'", "exc", err.Error(), "msg", message) + } +} + +func (ui *StdIOUI) ShowInfo(message string) { + err := ui.dispatch("ShowInfo", Message{message}, nil) + if err != nil { + log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message) + } +} +func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) { + err := ui.dispatch("OnApprovedTx", tx, nil) + if err != nil { + log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx) + } +} + +func (ui *StdIOUI) OnSignerStartup(info StartupInfo) { + err := ui.dispatch("OnSignerStartup", info, nil) + if err != nil { + log.Info("Error calling 'OnSignerStartup'", "exc", err.Error(), "info", info) + } +} diff --git a/signer/core/types.go b/signer/core/types.go new file mode 100644 index 0000000000..8386bd44e7 --- /dev/null +++ b/signer/core/types.go @@ -0,0 +1,95 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "encoding/json" + "strings" + + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +type Accounts []Account + +func (as Accounts) String() string { + var output []string + for _, a := range as { + output = append(output, a.String()) + } + return strings.Join(output, "\n") +} + +type Account struct { + Typ string `json:"type"` + URL accounts.URL `json:"url"` + Address common.Address `json:"address"` +} + +func (a Account) String() string { + s, err := json.Marshal(a) + if err == nil { + return string(s) + } + return err.Error() +} + +type ValidationInfo struct { + Typ string `json:"type"` + Message string `json:"message"` +} +type ValidationMessages struct { + Messages []ValidationInfo +} + +// SendTxArgs represents the arguments to submit a transaction +type SendTxArgs struct { + From common.MixedcaseAddress `json:"from"` + To *common.MixedcaseAddress `json:"to"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice hexutil.Big `json:"gasPrice"` + Value hexutil.Big `json:"value"` + Nonce hexutil.Uint64 `json:"nonce"` + // We accept "data" and "input" for backwards-compatibility reasons. + Data *hexutil.Bytes `json:"data"` + Input *hexutil.Bytes `json:"input"` +} + +func (t SendTxArgs) String() string { + s, err := json.Marshal(t) + if err == nil { + return string(s) + } + return err.Error() +} + +func (args *SendTxArgs) toTransaction() *types.Transaction { + var input []byte + if args.Data != nil { + input = *args.Data + } else if args.Input != nil { + input = *args.Input + } + if args.To == nil { + return types.NewContractCreation(uint64(args.Nonce), (*big.Int)(&args.Value), uint64(args.Gas), (*big.Int)(&args.GasPrice), input) + } + return types.NewTransaction(uint64(args.Nonce), args.To.Address(), (*big.Int)(&args.Value), (uint64)(args.Gas), (*big.Int)(&args.GasPrice), input) +} diff --git a/signer/core/validation.go b/signer/core/validation.go new file mode 100644 index 0000000000..97bb3b6850 --- /dev/null +++ b/signer/core/validation.go @@ -0,0 +1,163 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "bytes" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// The validation package contains validation checks for transactions +// - ABI-data validation +// - Transaction semantics validation +// The package provides warnings for typical pitfalls + +func (vs *ValidationMessages) crit(msg string) { + vs.Messages = append(vs.Messages, ValidationInfo{"CRITICAL", msg}) +} +func (vs *ValidationMessages) warn(msg string) { + vs.Messages = append(vs.Messages, ValidationInfo{"WARNING", msg}) +} +func (vs *ValidationMessages) info(msg string) { + vs.Messages = append(vs.Messages, ValidationInfo{"Info", msg}) +} + +type Validator struct { + db *AbiDb +} + +func NewValidator(db *AbiDb) *Validator { + return &Validator{db} +} +func testSelector(selector string, data []byte) (*decodedCallData, error) { + if selector == "" { + return nil, fmt.Errorf("selector not found") + } + abiData, err := MethodSelectorToAbi(selector) + if err != nil { + return nil, err + } + info, err := parseCallData(data, string(abiData)) + if err != nil { + return nil, err + } + return info, nil + +} + +// validateCallData checks if the ABI-data + methodselector (if given) can be parsed and seems to match +func (v *Validator) validateCallData(msgs *ValidationMessages, data []byte, methodSelector *string) { + if len(data) == 0 { + return + } + if len(data) < 4 { + msgs.warn("Tx contains data which is not valid ABI") + return + } + var ( + info *decodedCallData + err error + ) + // Check the provided one + if methodSelector != nil { + info, err = testSelector(*methodSelector, data) + if err != nil { + msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %v", err)) + } else { + msgs.info(info.String()) + //Successfull match. add to db if not there already (ignore errors there) + v.db.AddSignature(*methodSelector, data[:4]) + } + return + } + // Check the db + selector, err := v.db.LookupMethodSelector(data[:4]) + if err != nil { + msgs.warn(fmt.Sprintf("Tx contains data, but the ABI signature could not be found: %v", err)) + return + } + info, err = testSelector(selector, data) + if err != nil { + msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %v", err)) + } else { + msgs.info(info.String()) + } +} + +// validateSemantics checks if the transactions 'makes sense', and generate warnings for a couple of typical scenarios +func (v *Validator) validate(msgs *ValidationMessages, txargs *SendTxArgs, methodSelector *string) error { + // Prevent accidental erroneous usage of both 'input' and 'data' + if txargs.Data != nil && txargs.Input != nil && !bytes.Equal(*txargs.Data, *txargs.Input) { + // This is a showstopper + return errors.New(`Ambiguous request: both "data" and "input" are set and are not identical`) + } + var ( + data []byte + ) + // Place data on 'data', and nil 'input' + if txargs.Input != nil { + txargs.Data = txargs.Input + txargs.Input = nil + } + if txargs.Data != nil { + data = *txargs.Data + } + + if txargs.To == nil { + //Contract creation should contain sufficient data to deploy a contract + // A typical error is omitting sender due to some quirk in the javascript call + // e.g. https://github.com/ethereum/go-ethereum/issues/16106 + if len(data) == 0 { + if txargs.Value.ToInt().Cmp(big.NewInt(0)) > 0 { + // Sending ether into black hole + return errors.New(`Tx will create contract with value but empty code!`) + } + // No value submitted at least + msgs.crit("Tx will create contract with empty code!") + } else if len(data) < 40 { //Arbitrary limit + msgs.warn(fmt.Sprintf("Tx will will create contract, but payload is suspiciously small (%d b)", len(data))) + } + // methodSelector should be nil for contract creation + if methodSelector != nil { + msgs.warn("Tx will create contract, but method selector supplied; indicating intent to call a method.") + } + + } else { + if !txargs.To.ValidChecksum() { + msgs.warn("Invalid checksum on to-address") + } + // Normal transaction + if bytes.Equal(txargs.To.Address().Bytes(), common.Address{}.Bytes()) { + // Sending to 0 + msgs.crit("Tx destination is the zero address!") + } + // Validate calldata + v.validateCallData(msgs, data, methodSelector) + } + return nil +} + +// ValidateTransaction does a number of checks on the supplied transaction, and returns either a list of warnings, +// or an error, indicating that the transaction should be immediately rejected +func (v *Validator) ValidateTransaction(txArgs *SendTxArgs, methodSelector *string) (*ValidationMessages, error) { + msgs := &ValidationMessages{} + return msgs, v.validate(msgs, txArgs, methodSelector) +} diff --git a/signer/core/validation_test.go b/signer/core/validation_test.go new file mode 100644 index 0000000000..2b33a8630b --- /dev/null +++ b/signer/core/validation_test.go @@ -0,0 +1,139 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func hexAddr(a string) common.Address { return common.BytesToAddress(common.FromHex(a)) } +func mixAddr(a string) (*common.MixedcaseAddress, error) { + return common.NewMixedcaseAddressFromString(a) +} +func toHexBig(h string) hexutil.Big { + b := big.NewInt(0).SetBytes(common.FromHex(h)) + return hexutil.Big(*b) +} +func toHexUint(h string) hexutil.Uint64 { + b := big.NewInt(0).SetBytes(common.FromHex(h)) + return hexutil.Uint64(b.Uint64()) +} +func dummyTxArgs(t txtestcase) *SendTxArgs { + to, _ := mixAddr(t.to) + from, _ := mixAddr(t.from) + n := toHexUint(t.n) + gas := toHexUint(t.g) + gasPrice := toHexBig(t.gp) + value := toHexBig(t.value) + var ( + data, input *hexutil.Bytes + ) + if t.d != "" { + a := hexutil.Bytes(common.FromHex(t.d)) + data = &a + } + if t.i != "" { + a := hexutil.Bytes(common.FromHex(t.i)) + input = &a + + } + return &SendTxArgs{ + From: *from, + To: to, + Value: value, + Nonce: n, + GasPrice: gasPrice, + Gas: gas, + Data: data, + Input: input, + } +} + +type txtestcase struct { + from, to, n, g, gp, value, d, i string + expectErr bool + numMessages int +} + +func TestValidator(t *testing.T) { + var ( + // use empty db, there are other tests for the abi-specific stuff + db, _ = NewEmptyAbiDB() + v = NewValidator(db) + ) + testcases := []txtestcase{ + // Invalid to checksum + {from: "000000000000000000000000000000000000dead", to: "000000000000000000000000000000000000dead", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 1}, + // valid 0x000000000000000000000000000000000000dEaD + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 0}, + // conflicting input and data + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x01", i: "0x02", expectErr: true}, + // Data can't be parsed + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x0102", numMessages: 1}, + // Data (on Input) can't be parsed + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", i: "0x0102", numMessages: 1}, + // Send to 0 + {from: "000000000000000000000000000000000000dead", to: "0x0000000000000000000000000000000000000000", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 1}, + // Create empty contract (no value) + {from: "000000000000000000000000000000000000dead", to: "", + n: "0x01", g: "0x20", gp: "0x40", value: "0x00", numMessages: 1}, + // Create empty contract (with value) + {from: "000000000000000000000000000000000000dead", to: "", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", expectErr: true}, + // Small payload for create + {from: "000000000000000000000000000000000000dead", to: "", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x01", numMessages: 1}, + } + for i, test := range testcases { + msgs, err := v.ValidateTransaction(dummyTxArgs(test), nil) + if err == nil && test.expectErr { + t.Errorf("Test %d, expected error", i) + for _, msg := range msgs.Messages { + fmt.Printf("* %s: %s\n", msg.Typ, msg.Message) + } + } + if err != nil && !test.expectErr { + t.Errorf("Test %d, unexpected error: %v", i, err) + } + if err == nil { + got := len(msgs.Messages) + if got != test.numMessages { + for _, msg := range msgs.Messages { + fmt.Printf("* %s: %s\n", msg.Typ, msg.Message) + } + t.Errorf("Test %d, expected %d messages, got %d", i, test.numMessages, got) + } else { + //Debug printout, remove later + for _, msg := range msgs.Messages { + fmt.Printf("* [%d] %s: %s\n", i, msg.Typ, msg.Message) + } + fmt.Println() + } + } + } +} diff --git a/signer/rules/deps/bignumber.js b/signer/rules/deps/bignumber.js new file mode 100644 index 0000000000..17c8851e24 --- /dev/null +++ b/signer/rules/deps/bignumber.js @@ -0,0 +1,4 @@ +/* bignumber.js v2.0.3 https://github.com/MikeMcl/bignumber.js/LICENCE */ +/* modified by zelig to fix https://github.com/robertkrimen/otto#regular-expression-incompatibility */ +!function(e){"use strict";function n(e){function a(e,n){var t,r,i,o,u,s,f=this;if(!(f instanceof a))return j&&L(26,"constructor call without new",e),new a(e,n);if(null!=n&&H(n,2,64,M,"base")){if(n=0|n,s=e+"",10==n)return f=new a(e instanceof a?e:s),U(f,P+f.e+1,k);if((o="number"==typeof e)&&0*e!=0||!new RegExp("^-?"+(t="["+O.slice(0,n)+"]+")+"(?:\\."+t+")?$",37>n?"i":"").test(s))return g(f,s,o,n);o?(f.s=0>1/e?(s=s.slice(1),-1):1,j&&s.replace(/^0\.0*|\./,"").length>15&&L(M,b,e),o=!1):f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1,s=D(s,10,n,f.s)}else{if(e instanceof a)return f.s=e.s,f.e=e.e,f.c=(e=e.c)?e.slice():e,void(M=0);if((o="number"==typeof e)&&0*e==0){if(f.s=0>1/e?(e=-e,-1):1,e===~~e){for(r=0,i=e;i>=10;i/=10,r++);return f.e=r,f.c=[e],void(M=0)}s=e+""}else{if(!p.test(s=e+""))return g(f,s,o);f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1}}for((r=s.indexOf("."))>-1&&(s=s.replace(".","")),(i=s.search(/e/i))>0?(0>r&&(r=i),r+=+s.slice(i+1),s=s.substring(0,i)):0>r&&(r=s.length),i=0;48===s.charCodeAt(i);i++);for(u=s.length;48===s.charCodeAt(--u););if(s=s.slice(i,u+1))if(u=s.length,o&&j&&u>15&&L(M,b,f.s*e),r=r-i-1,r>z)f.c=f.e=null;else if(G>r)f.c=[f.e=0];else{if(f.e=r,f.c=[],i=(r+1)%y,0>r&&(i+=y),u>i){for(i&&f.c.push(+s.slice(0,i)),u-=y;u>i;)f.c.push(+s.slice(i,i+=y));s=s.slice(i),i=y-s.length}else i-=u;for(;i--;s+="0");f.c.push(+s)}else f.c=[f.e=0];M=0}function D(e,n,t,i){var o,u,f,c,h,g,p,d=e.indexOf("."),m=P,w=k;for(37>t&&(e=e.toLowerCase()),d>=0&&(f=J,J=0,e=e.replace(".",""),p=new a(t),h=p.pow(e.length-d),J=f,p.c=s(l(r(h.c),h.e),10,n),p.e=p.c.length),g=s(e,t,n),u=f=g.length;0==g[--f];g.pop());if(!g[0])return"0";if(0>d?--u:(h.c=g,h.e=u,h.s=i,h=C(h,p,m,w,n),g=h.c,c=h.r,u=h.e),o=u+m+1,d=g[o],f=n/2,c=c||0>o||null!=g[o+1],c=4>w?(null!=d||c)&&(0==w||w==(h.s<0?3:2)):d>f||d==f&&(4==w||c||6==w&&1&g[o-1]||w==(h.s<0?8:7)),1>o||!g[0])e=c?l("1",-m):"0";else{if(g.length=o,c)for(--n;++g[--o]>n;)g[o]=0,o||(++u,g.unshift(1));for(f=g.length;!g[--f];);for(d=0,e="";f>=d;e+=O.charAt(g[d++]));e=l(e,u)}return e}function _(e,n,t,i){var o,u,s,c,h;if(t=null!=t&&H(t,0,8,i,v)?0|t:k,!e.c)return e.toString();if(o=e.c[0],s=e.e,null==n)h=r(e.c),h=19==i||24==i&&B>=s?f(h,s):l(h,s);else if(e=U(new a(e),n,t),u=e.e,h=r(e.c),c=h.length,19==i||24==i&&(u>=n||B>=u)){for(;n>c;h+="0",c++);h=f(h,u)}else if(n-=s,h=l(h,u),u+1>c){if(--n>0)for(h+=".";n--;h+="0");}else if(n+=u-c,n>0)for(u+1==c&&(h+=".");n--;h+="0");return e.s<0&&o?"-"+h:h}function x(e,n){var t,r,i=0;for(u(e[0])&&(e=e[0]),t=new a(e[0]);++ie||e>t||e!=c(e))&&L(r,(i||"decimal places")+(n>e||e>t?" out of range":" not an integer"),e),!0}function I(e,n,t){for(var r=1,i=n.length;!n[--i];n.pop());for(i=n[0];i>=10;i/=10,r++);return(t=r+t*y-1)>z?e.c=e.e=null:G>t?e.c=[e.e=0]:(e.e=t,e.c=n),e}function L(e,n,t){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][e]+"() "+n+": "+t);throw r.name="BigNumber Error",M=0,r}function U(e,n,t,r){var i,o,u,s,f,l,c,a=e.c,h=R;if(a){e:{for(i=1,s=a[0];s>=10;s/=10,i++);if(o=n-i,0>o)o+=y,u=n,f=a[l=0],c=f/h[i-u-1]%10|0;else if(l=d((o+1)/y),l>=a.length){if(!r)break e;for(;a.length<=l;a.push(0));f=c=0,i=1,o%=y,u=o-y+1}else{for(f=s=a[l],i=1;s>=10;s/=10,i++);o%=y,u=o-y+i,c=0>u?0:f/h[i-u-1]%10|0}if(r=r||0>n||null!=a[l+1]||(0>u?f:f%h[i-u-1]),r=4>t?(c||r)&&(0==t||t==(e.s<0?3:2)):c>5||5==c&&(4==t||r||6==t&&(o>0?u>0?f/h[i-u]:0:a[l-1])%10&1||t==(e.s<0?8:7)),1>n||!a[0])return a.length=0,r?(n-=e.e+1,a[0]=h[n%y],e.e=-n||0):a[0]=e.e=0,e;if(0==o?(a.length=l,s=1,l--):(a.length=l+1,s=h[y-o],a[l]=u>0?m(f/h[i-u]%h[u])*s:0),r)for(;;){if(0==l){for(o=1,u=a[0];u>=10;u/=10,o++);for(u=a[0]+=s,s=1;u>=10;u/=10,s++);o!=s&&(e.e++,a[0]==N&&(a[0]=1));break}if(a[l]+=s,a[l]!=N)break;a[l--]=0,s=1}for(o=a.length;0===a[--o];a.pop());}e.e>z?e.c=e.e=null:e.et?null!=(e=i[t++]):void 0};return f(n="DECIMAL_PLACES")&&H(e,0,E,2,n)&&(P=0|e),r[n]=P,f(n="ROUNDING_MODE")&&H(e,0,8,2,n)&&(k=0|e),r[n]=k,f(n="EXPONENTIAL_AT")&&(u(e)?H(e[0],-E,0,2,n)&&H(e[1],0,E,2,n)&&(B=0|e[0],$=0|e[1]):H(e,-E,E,2,n)&&(B=-($=0|(0>e?-e:e)))),r[n]=[B,$],f(n="RANGE")&&(u(e)?H(e[0],-E,-1,2,n)&&H(e[1],1,E,2,n)&&(G=0|e[0],z=0|e[1]):H(e,-E,E,2,n)&&(0|e?G=-(z=0|(0>e?-e:e)):j&&L(2,n+" cannot be zero",e))),r[n]=[G,z],f(n="ERRORS")&&(e===!!e||1===e||0===e?(M=0,H=(j=!!e)?F:o):j&&L(2,n+w,e)),r[n]=j,f(n="CRYPTO")&&(e===!!e||1===e||0===e?(V=!(!e||!h||"object"!=typeof h),e&&!V&&j&&L(2,"crypto unavailable",h)):j&&L(2,n+w,e)),r[n]=V,f(n="MODULO_MODE")&&H(e,0,9,2,n)&&(W=0|e),r[n]=W,f(n="POW_PRECISION")&&H(e,0,E,2,n)&&(J=0|e),r[n]=J,f(n="FORMAT")&&("object"==typeof e?X=e:j&&L(2,n+" not an object",e)),r[n]=X,r},a.max=function(){return x(arguments,T.lt)},a.min=function(){return x(arguments,T.gt)},a.random=function(){var e=9007199254740992,n=Math.random()*e&2097151?function(){return m(Math.random()*e)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(e){var t,r,i,o,u,s=0,f=[],l=new a(q);if(e=null!=e&&H(e,0,E,14)?0|e:P,o=d(e/y),V)if(h&&h.getRandomValues){for(t=h.getRandomValues(new Uint32Array(o*=2));o>s;)u=131072*t[s]+(t[s+1]>>>11),u>=9e15?(r=h.getRandomValues(new Uint32Array(2)),t[s]=r[0],t[s+1]=r[1]):(f.push(u%1e14),s+=2);s=o/2}else if(h&&h.randomBytes){for(t=h.randomBytes(o*=7);o>s;)u=281474976710656*(31&t[s])+1099511627776*t[s+1]+4294967296*t[s+2]+16777216*t[s+3]+(t[s+4]<<16)+(t[s+5]<<8)+t[s+6],u>=9e15?h.randomBytes(7).copy(t,s):(f.push(u%1e14),s+=7);s=o/7}else j&&L(14,"crypto unavailable",h);if(!s)for(;o>s;)u=n(),9e15>u&&(f[s++]=u%1e14);for(o=f[--s],e%=y,o&&e&&(u=R[y-e],f[s]=m(o/u)*u);0===f[s];f.pop(),s--);if(0>s)f=[i=0];else{for(i=-1;0===f[0];f.shift(),i-=y);for(s=1,u=f[0];u>=10;u/=10,s++);y>s&&(i-=y-s)}return l.e=i,l.c=f,l}}(),C=function(){function e(e,n,t){var r,i,o,u,s=0,f=e.length,l=n%A,c=n/A|0;for(e=e.slice();f--;)o=e[f]%A,u=e[f]/A|0,r=c*o+u*l,i=l*o+r%A*A+s,s=(i/t|0)+(r/A|0)+c*u,e[f]=i%t;return s&&e.unshift(s),e}function n(e,n,t,r){var i,o;if(t!=r)o=t>r?1:-1;else for(i=o=0;t>i;i++)if(e[i]!=n[i]){o=e[i]>n[i]?1:-1;break}return o}function r(e,n,t,r){for(var i=0;t--;)e[t]-=i,i=e[t]1;e.shift());}return function(i,o,u,s,f){var l,c,h,g,p,d,w,v,b,O,S,R,A,E,D,_,x,F=i.s==o.s?1:-1,I=i.c,L=o.c;if(!(I&&I[0]&&L&&L[0]))return new a(i.s&&o.s&&(I?!L||I[0]!=L[0]:L)?I&&0==I[0]||!L?0*F:F/0:0/0);for(v=new a(F),b=v.c=[],c=i.e-o.e,F=u+c+1,f||(f=N,c=t(i.e/y)-t(o.e/y),F=F/y|0),h=0;L[h]==(I[h]||0);h++);if(L[h]>(I[h]||0)&&c--,0>F)b.push(1),g=!0;else{for(E=I.length,_=L.length,h=0,F+=2,p=m(f/(L[0]+1)),p>1&&(L=e(L,p,f),I=e(I,p,f),_=L.length,E=I.length),A=_,O=I.slice(0,_),S=O.length;_>S;O[S++]=0);x=L.slice(),x.unshift(0),D=L[0],L[1]>=f/2&&D++;do p=0,l=n(L,O,_,S),0>l?(R=O[0],_!=S&&(R=R*f+(O[1]||0)),p=m(R/D),p>1?(p>=f&&(p=f-1),d=e(L,p,f),w=d.length,S=O.length,l=n(d,O,w,S),1==l&&(p--,r(d,w>_?x:L,w,f))):(0==p&&(l=p=1),d=L.slice()),w=d.length,S>w&&d.unshift(0),r(O,d,S,f),-1==l&&(S=O.length,l=n(L,O,_,S),1>l&&(p++,r(O,S>_?x:L,S,f))),S=O.length):0===l&&(p++,O=[0]),b[h++]=p,l&&O[0]?O[S++]=I[A]||0:(O=[I[A]],S=1);while((A++=10;F/=10,h++);U(v,u+(v.e=h+c*y-1)+1,s,g)}else v.e=c,v.r=+g;return v}}(),g=function(){var e=/^(-?)0([xbo])(\w[\w.]*$)/i,n=/^([^.]+)\.$/,t=/^\.([^.]+)$/,r=/^-?(Infinity|NaN)$/,i=/^\s*\+([\w.])|^\s+|\s+$/g;return function(o,u,s,f){var l,c=s?u:u.replace(i,"$1");if(r.test(c))o.s=isNaN(c)?null:0>c?-1:1;else{if(!s&&(c=c.replace(e,function(e,n,t){return l="x"==(t=t.toLowerCase())?16:"b"==t?2:8,f&&f!=l?e:n}),f&&(l=f,c=c.replace(n,"$1").replace(t,"0.$1")),u!=c))return new a(c,l);j&&L(M,"not a"+(f?" base "+f:"")+" number",u),o.s=null}o.c=o.e=null,M=0}}(),T.absoluteValue=T.abs=function(){var e=new a(this);return e.s<0&&(e.s=1),e},T.ceil=function(){return U(new a(this),this.e+1,2)},T.comparedTo=T.cmp=function(e,n){return M=1,i(this,new a(e,n))},T.decimalPlaces=T.dp=function(){var e,n,r=this.c;if(!r)return null;if(e=((n=r.length-1)-t(this.e/y))*y,n=r[n])for(;n%10==0;n/=10,e--);return 0>e&&(e=0),e},T.dividedBy=T.div=function(e,n){return M=3,C(this,new a(e,n),P,k)},T.dividedToIntegerBy=T.divToInt=function(e,n){return M=4,C(this,new a(e,n),0,1)},T.equals=T.eq=function(e,n){return M=5,0===i(this,new a(e,n))},T.floor=function(){return U(new a(this),this.e+1,3)},T.greaterThan=T.gt=function(e,n){return M=6,i(this,new a(e,n))>0},T.greaterThanOrEqualTo=T.gte=function(e,n){return M=7,1===(n=i(this,new a(e,n)))||0===n},T.isFinite=function(){return!!this.c},T.isInteger=T.isInt=function(){return!!this.c&&t(this.e/y)>this.c.length-2},T.isNaN=function(){return!this.s},T.isNegative=T.isNeg=function(){return this.s<0},T.isZero=function(){return!!this.c&&0==this.c[0]},T.lessThan=T.lt=function(e,n){return M=8,i(this,new a(e,n))<0},T.lessThanOrEqualTo=T.lte=function(e,n){return M=9,-1===(n=i(this,new a(e,n)))||0===n},T.minus=T.sub=function(e,n){var r,i,o,u,s=this,f=s.s;if(M=10,e=new a(e,n),n=e.s,!f||!n)return new a(0/0);if(f!=n)return e.s=-n,s.plus(e);var l=s.e/y,c=e.e/y,h=s.c,g=e.c;if(!l||!c){if(!h||!g)return h?(e.s=-n,e):new a(g?s:0/0);if(!h[0]||!g[0])return g[0]?(e.s=-n,e):new a(h[0]?s:3==k?-0:0)}if(l=t(l),c=t(c),h=h.slice(),f=l-c){for((u=0>f)?(f=-f,o=h):(c=l,o=g),o.reverse(),n=f;n--;o.push(0));o.reverse()}else for(i=(u=(f=h.length)<(n=g.length))?f:n,f=n=0;i>n;n++)if(h[n]!=g[n]){u=h[n]0)for(;n--;h[r++]=0);for(n=N-1;i>f;){if(h[--i]0?(s=u,r=l):(o=-o,r=f),r.reverse();o--;r.push(0));r.reverse()}for(o=f.length,n=l.length,0>o-n&&(r=l,l=f,f=r,n=o),o=0;n;)o=(f[--n]=f[n]+l[n]+o)/N|0,f[n]%=N;return o&&(f.unshift(o),++s),I(e,f,s)},T.precision=T.sd=function(e){var n,t,r=this,i=r.c;if(null!=e&&e!==!!e&&1!==e&&0!==e&&(j&&L(13,"argument"+w,e),e!=!!e&&(e=null)),!i)return null;if(t=i.length-1,n=t*y+1,t=i[t]){for(;t%10==0;t/=10,n--);for(t=i[0];t>=10;t/=10,n++);}return e&&r.e+1>n&&(n=r.e+1),n},T.round=function(e,n){var t=new a(this);return(null==e||H(e,0,E,15))&&U(t,~~e+this.e+1,null!=n&&H(n,0,8,15,v)?0|n:k),t},T.shift=function(e){var n=this;return H(e,-S,S,16,"argument")?n.times("1e"+c(e)):new a(n.c&&n.c[0]&&(-S>e||e>S)?n.s*(0>e?0:1/0):n)},T.squareRoot=T.sqrt=function(){var e,n,i,o,u,s=this,f=s.c,l=s.s,c=s.e,h=P+4,g=new a("0.5");if(1!==l||!f||!f[0])return new a(!l||0>l&&(!f||f[0])?0/0:f?s:1/0);if(l=Math.sqrt(+s),0==l||l==1/0?(n=r(f),(n.length+c)%2==0&&(n+="0"),l=Math.sqrt(n),c=t((c+1)/2)-(0>c||c%2),l==1/0?n="1e"+c:(n=l.toExponential(),n=n.slice(0,n.indexOf("e")+1)+c),i=new a(n)):i=new a(l+""),i.c[0])for(c=i.e,l=c+h,3>l&&(l=0);;)if(u=i,i=g.times(u.plus(C(s,u,h,1))),r(u.c).slice(0,l)===(n=r(i.c)).slice(0,l)){if(i.el&&(m=O,O=S,S=m,o=l,l=g,g=o),o=l+g,m=[];o--;m.push(0));for(w=N,v=A,o=g;--o>=0;){for(r=0,p=S[o]%v,d=S[o]/v|0,s=l,u=o+s;u>o;)c=O[--s]%v,h=O[s]/v|0,f=d*c+h*p,c=p*c+f%v*v+m[u]+r,r=(c/w|0)+(f/v|0)+d*h,m[u--]=c%w;m[u]=r}return r?++i:m.shift(),I(e,m,i)},T.toDigits=function(e,n){var t=new a(this);return e=null!=e&&H(e,1,E,18,"precision")?0|e:null,n=null!=n&&H(n,0,8,18,v)?0|n:k,e?U(t,e,n):t},T.toExponential=function(e,n){return _(this,null!=e&&H(e,0,E,19)?~~e+1:null,n,19)},T.toFixed=function(e,n){return _(this,null!=e&&H(e,0,E,20)?~~e+this.e+1:null,n,20)},T.toFormat=function(e,n){var t=_(this,null!=e&&H(e,0,E,21)?~~e+this.e+1:null,n,21);if(this.c){var r,i=t.split("."),o=+X.groupSize,u=+X.secondaryGroupSize,s=X.groupSeparator,f=i[0],l=i[1],c=this.s<0,a=c?f.slice(1):f,h=a.length;if(u&&(r=o,o=u,u=r,h-=r),o>0&&h>0){for(r=h%o||o,f=a.substr(0,r);h>r;r+=o)f+=s+a.substr(r,o);u>0&&(f+=s+a.slice(r)),c&&(f="-"+f)}t=l?f+X.decimalSeparator+((u=+X.fractionGroupSize)?l.replace(new RegExp("\\d{"+u+"}\\B","g"),"$&"+X.fractionGroupSeparator):l):f}return t},T.toFraction=function(e){var n,t,i,o,u,s,f,l,c,h=j,g=this,p=g.c,d=new a(q),m=t=new a(q),w=f=new a(q);if(null!=e&&(j=!1,s=new a(e),j=h,(!(h=s.isInt())||s.lt(q))&&(j&&L(22,"max denominator "+(h?"out of range":"not an integer"),e),e=!h&&s.c&&U(s,s.e+1,1).gte(q)?s:null)),!p)return g.toString();for(c=r(p),o=d.e=c.length-g.e-1,d.c[0]=R[(u=o%y)<0?y+u:u],e=!e||s.cmp(d)>0?o>0?d:m:s,u=z,z=1/0,s=new a(c),f.c[0]=0;l=C(s,d,0,1),i=t.plus(l.times(w)),1!=i.cmp(e);)t=w,w=i,m=f.plus(l.times(i=m)),f=i,d=s.minus(l.times(i=d)),s=i;return i=C(e.minus(t),w,0,1),f=f.plus(i.times(m)),t=t.plus(i.times(w)),f.s=m.s=g.s,o*=2,n=C(m,w,o,k).minus(g).abs().cmp(C(f,t,o,k).minus(g).abs())<1?[m.toString(),w.toString()]:[f.toString(),t.toString()],z=u,n},T.toNumber=function(){var e=this;return+e||(e.s?0*e.s:0/0)},T.toPower=T.pow=function(e){var n,t,r=m(0>e?-e:+e),i=this;if(!H(e,-S,S,23,"exponent")&&(!isFinite(e)||r>S&&(e/=0)||parseFloat(e)!=e&&!(e=0/0)))return new a(Math.pow(+i,e));for(n=J?d(J/y+2):0,t=new a(q);;){if(r%2){if(t=t.times(i),!t.c)break;n&&t.c.length>n&&(t.c.length=n)}if(r=m(r/2),!r)break;i=i.times(i),n&&i.c&&i.c.length>n&&(i.c.length=n)}return 0>e&&(t=q.div(t)),n?U(t,J,k):t},T.toPrecision=function(e,n){return _(this,null!=e&&H(e,1,E,24,"precision")?0|e:null,n,24)},T.toString=function(e){var n,t=this,i=t.s,o=t.e;return null===o?i?(n="Infinity",0>i&&(n="-"+n)):n="NaN":(n=r(t.c),n=null!=e&&H(e,2,64,25,"base")?D(l(n,o),0|e,10,i):B>=o||o>=$?f(n,o):l(n,o),0>i&&t.c[0]&&(n="-"+n)),n},T.truncated=T.trunc=function(){return U(new a(this),this.e+1,1)},T.valueOf=T.toJSON=function(){return this.toString()},null!=e&&a.config(e),a}function t(e){var n=0|e;return e>0||e===n?n:n-1}function r(e){for(var n,t,r=1,i=e.length,o=e[0]+"";i>r;){for(n=e[r++]+"",t=y-n.length;t--;n="0"+n);o+=n}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function i(e,n){var t,r,i=e.c,o=n.c,u=e.s,s=n.s,f=e.e,l=n.e;if(!u||!s)return null;if(t=i&&!i[0],r=o&&!o[0],t||r)return t?r?0:-s:u;if(u!=s)return u;if(t=0>u,r=f==l,!i||!o)return r?0:!i^t?1:-1;if(!r)return f>l^t?1:-1;for(s=(f=i.length)<(l=o.length)?f:l,u=0;s>u;u++)if(i[u]!=o[u])return i[u]>o[u]^t?1:-1;return f==l?0:f>l^t?1:-1}function o(e,n,t){return(e=c(e))>=n&&t>=e}function u(e){return"[object Array]"==Object.prototype.toString.call(e)}function s(e,n,t){for(var r,i,o=[0],u=0,s=e.length;s>u;){for(i=o.length;i--;o[i]*=n);for(o[r=0]+=O.indexOf(e.charAt(u++));rt-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/t|0,o[r]%=t)}return o.reverse()}function f(e,n){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(0>n?"e":"e+")+n}function l(e,n){var t,r;if(0>n){for(r="0.";++n;r+="0");e=r+e}else if(t=e.length,++n>t){for(r="0",n-=t;--n;r+="0");e+=r}else t>n&&(e=e.slice(0,n)+"."+e.slice(n));return e}function c(e){return e=parseFloat(e),0>e?d(e):m(e)}var a,h,g,p=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,d=Math.ceil,m=Math.floor,w=" not a boolean or binary digit",v="rounding mode",b="number type has more than 15 significant digits",O="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",N=1e14,y=14,S=9007199254740991,R=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],A=1e7,E=1e9;if(a=n(),"function"==typeof define&&define.amd)define(function(){return a});else if("undefined"!=typeof module&&module.exports){if(module.exports=a,!h)try{h=require("crypto")}catch(D){}}else e.BigNumber=a}(this); +//# sourceMappingURL=doc/bignumber.js.map diff --git a/signer/rules/deps/bindata.go b/signer/rules/deps/bindata.go new file mode 100644 index 0000000000..0b27f45172 --- /dev/null +++ b/signer/rules/deps/bindata.go @@ -0,0 +1,235 @@ +// Code generated by go-bindata. +// sources: +// bignumber.js +// DO NOT EDIT! + +package deps + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _bignumberJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xbc\x6b\x77\x9b\xc8\x93\x38\xfc\x7e\x3f\x85\xc4\xc6\x9c\x6e\x53\x20\x90\x9d\x38\x86\x14\x9c\x4c\x62\xe7\xe7\x79\x1c\x3b\x4f\x9c\xcc\xcc\xae\xa2\xc9\x91\x51\x23\x75\x82\x40\xe1\x62\xc7\x09\xfe\x7d\xf6\xff\xa9\x6e\x40\xf2\x25\xbb\xb3\x6f\x2c\xe8\x4b\x75\x75\x75\xdd\xbb\xf0\x68\x77\x70\x29\x17\x59\xbd\xba\x14\x85\xf3\xa5\x1c\x5c\x8d\x1d\xd7\xd9\x1b\x2c\xab\x6a\x5d\xfa\xa3\xd1\x42\x56\xcb\xfa\xd2\x89\xf3\xd5\xe8\xad\xfc\x2a\xde\xc6\xe9\x68\x7b\xf8\xe8\xf4\xe4\xd5\xd1\xd9\xab\xa3\xc1\xee\xe8\x3f\x46\xbb\x83\x55\x3e\x97\x89\x14\xf3\xc1\xe5\xcd\xe0\x87\x48\xe5\x62\x50\xe5\x83\x44\x7e\x7f\x0c\x5c\x91\x5f\x8a\xa2\xfa\x5a\xc8\x95\xc8\x46\x79\x55\xe5\xff\x59\x88\x45\x9d\xce\x0a\x5b\x7c\x5f\x17\xa2\x2c\x65\x9e\xd9\x32\x8b\xf3\xd5\x7a\x56\xc9\x4b\x99\xca\xea\x86\x96\x19\x26\x75\x16\x57\x32\xcf\x98\xe0\x3f\x8d\xba\x14\x83\xb2\x2a\x64\x5c\x19\x41\xd7\x31\x50\x5d\xfd\xdb\x8c\x09\xc8\xf8\xcf\xab\x59\x31\xa8\xa0\x00\x09\x39\xd4\x50\x42\x82\xd5\x52\x96\x81\x4c\xd8\x90\x25\x03\x99\x95\xd5\x2c\x8b\x45\x9e\x0c\x66\x9c\x17\xa2\xaa\x8b\x6c\xf0\xc5\x34\x4f\xd9\xf8\x19\x18\x71\x9e\x95\x55\x51\xc7\x55\x5e\x0c\xe2\x59\x9a\x0e\xae\x65\xb5\xcc\xeb\x6a\x90\x89\x6b\x03\x04\x87\x4c\x5c\xb7\xeb\x10\xc0\xac\x4e\xd3\x21\x66\xa6\xf9\x2f\x96\xc1\x18\x9e\xed\xc3\x5b\x30\x2e\x67\xa5\x30\x38\xff\x49\xfd\xe8\x36\x19\x94\x28\x2c\xc3\x00\xcf\x45\xcc\xba\x15\x13\x6c\x21\xdd\x41\x28\x12\x7e\xc9\xe1\x23\x4b\xe0\x9d\x95\x38\xc2\xf2\xe0\xab\x5a\x87\xe5\x68\xe8\xa3\x30\x10\xab\x9b\x35\x0d\x16\xdc\x34\xdd\x5d\x31\x44\xb7\x69\x86\x04\xec\xbd\x58\x1c\x7d\x5f\x33\xe3\x6f\x3b\x32\x2c\x56\xa1\x31\x31\xac\x73\xa7\x4c\x65\x2c\x98\x0b\x19\xb7\x8c\xa9\x65\x70\xcb\x60\x91\xff\xe9\x93\x63\x58\x95\x65\xf0\xe8\x89\x01\x7b\x07\x61\x16\x19\xd2\xf0\x0d\x83\x3b\x95\x28\x2b\x56\xf6\x84\x59\xb0\x04\x4a\xc8\x69\xbb\x79\xc4\x12\xa7\x44\x37\xf4\x46\x22\x62\x25\x96\x2d\x68\x8f\x83\xed\x71\xdf\x83\x2f\xa6\x59\x3a\x85\x58\xa7\xb3\x58\xb0\xd1\xdf\xee\x27\xc7\xdd\x6d\x3e\x39\x23\x20\xb8\xa9\xc8\x16\xd5\x32\xf4\x9e\x12\xa5\xdf\xc2\x25\xd1\x32\xc7\xa1\xc7\x7d\x02\xba\xff\x14\x11\x4b\x27\x5e\xce\x8a\x57\xf9\x5c\xbc\xac\x98\xcb\x1f\x5d\xa3\xc4\xd7\xac\x04\xcf\x85\x0c\x12\xa7\xe4\xb7\x22\x2d\x05\x11\xfa\x2e\x19\x7b\x22\x3b\x25\x0a\xa7\x84\xc4\x11\x28\x1c\x01\x89\x13\x23\xa3\xc7\x98\x47\xa2\x05\xcd\x7d\x01\x57\xb9\x9c\xb3\xb7\xe8\xfe\x6f\xb4\x46\x74\xd5\xb1\x6e\xd1\x41\xa0\x2d\x5a\xdc\x04\x22\xfe\xfb\xdf\xc4\x90\x79\xc1\x0a\x74\x41\xa2\x08\x64\x88\x9e\x1b\xc8\x11\x7a\x2e\x14\x96\xc5\x83\x1e\x35\x81\x85\x42\x68\x22\xa6\x1b\x04\x6e\x35\xaf\xf4\xfb\x1a\xae\xdb\x13\x51\xcd\xf7\x8f\x85\x07\xff\x17\xe2\xdd\xde\x12\x62\xac\xc0\xd2\x91\xd9\x5c\x7c\x3f\x4f\x98\xe1\x18\x9c\x87\xb6\x67\x9a\x6a\x7c\x77\x78\x86\x63\xd0\xa1\x71\x60\x92\xa0\x88\x59\x11\x2f\xd9\x48\x8c\x24\xe7\xa1\x1b\x31\x37\x2c\x4c\x93\x15\x28\x39\x14\x16\x5a\xdd\x3a\xd2\xf2\x38\xa8\x65\xeb\x4b\x92\xd4\x6c\xc1\x5c\x90\x9c\xfb\xdd\xf8\xb2\xe5\x02\x0e\x12\xdd\x60\xff\xf9\x7d\xb4\x25\x0f\x24\x91\x88\xd0\xac\xfb\xd1\x8f\x0c\xb4\xed\x9a\x07\xea\xb0\x36\xbb\x94\x50\x5b\x1e\xe7\x32\xd9\x9a\x0a\xb9\x69\x7e\x31\xcd\x7a\x8b\xed\x12\xa7\xdc\x15\x1c\x0a\x2c\x6c\x69\x7b\x50\x84\x3f\x38\x1d\x02\x1d\x07\x09\x73\x40\x84\x1f\xc8\x84\xbd\x09\x0b\xd5\x31\xa1\x1e\x77\x1a\x74\x07\xb2\x75\x6e\x53\x90\xc8\x0a\xcb\xe3\x3b\x37\xa0\xb7\x28\x2d\xbc\xe1\x50\x87\x52\xf3\x80\x34\xcd\xc4\x89\x9d\x75\x5d\x2e\x59\x4f\x25\x45\x12\xa8\x6d\xbc\x09\xea\x50\x06\xfc\xe1\x08\x09\x0a\x0e\x0f\xb6\x36\x47\x24\xbb\xb1\xbb\x7d\xdd\x6a\x2c\x6d\xac\x15\xad\x02\x69\xdb\x41\x69\xa1\xe1\x1a\xc4\x11\x3d\x3c\x2d\x1e\x83\xed\x6d\xbc\x45\xf7\xb6\xd7\x97\xaf\x49\x8f\x41\x05\x52\xeb\x4c\xd2\x96\x09\xc4\xb0\x84\x05\xac\x61\x8e\xe2\x0e\x9b\xc0\x0a\xdf\xc1\x35\x7e\x55\x2b\xee\x1d\x84\x95\x69\x2a\x51\xaa\xf2\xd3\xfc\x5a\x14\xaf\x66\xa5\x60\x9c\xc3\x3c\x44\xd7\x34\x59\x82\xbf\xc3\xef\xe8\x02\x8d\xb8\xc7\x55\xb0\x6e\x55\x5f\xc5\x61\x89\x6b\x67\x9d\x5f\x33\xd1\x6e\xcc\x9e\x73\xf8\x1d\x13\x58\x3b\x31\x96\x2c\x65\x05\x5b\x3a\x31\x87\xa5\x23\xb8\x12\x7a\x0e\x6b\x47\xe0\xda\x89\x7b\x4e\x5a\x60\xc9\x04\x54\xd4\x55\x63\x82\x8b\x8e\x69\x5c\xc4\xc5\xc4\xb6\x93\x69\xb0\x70\xd6\xf9\x9a\x71\xc5\x2e\xc3\xc5\xc4\x9d\xb6\x42\x64\xb8\x06\x35\xb9\xe1\x3c\xb2\xed\xda\xa7\x95\x70\x41\x4b\x61\x0d\x4b\xa7\x44\x09\x4b\x7c\xc5\x96\xb0\x86\x15\x5c\x13\xfc\x05\x2e\x9d\x18\x62\x5c\x3a\x05\xd4\xa8\x70\xca\xb1\xb6\x56\x96\x07\x73\x5c\x4c\xf2\x29\x24\x98\x8d\xc6\x10\x63\xdc\x34\x6e\x98\x37\x8d\x36\x0f\x8b\x49\x6e\x79\x53\x88\x71\x3f\xbc\x8e\x5a\x93\x31\x6f\x9a\x98\x9b\x26\x73\x11\xaf\x9b\xe6\x1a\x91\x2d\x9d\xf2\x85\x1b\xed\xf9\x63\xce\xfd\x79\x98\x34\xcd\x1c\x31\x31\x4d\xb6\xaf\x46\xc4\x4d\xf3\x0c\xf1\xda\x34\x3d\x73\x31\xc9\x6d\x6f\xba\x3d\xe9\xb9\x7f\xc0\x39\x78\xb4\xa2\xde\xa0\xc0\x38\x4a\x99\xe1\x19\x60\xaf\xb8\x4f\x1b\xed\xd8\xb7\xa3\x0f\xe6\x10\x73\x3a\x49\xdb\xce\x02\xcb\x22\x52\xe5\xd3\x30\x0b\x38\xed\x03\x5d\xc8\x9b\x86\x59\x56\x0d\x0b\xa7\xce\xca\xa5\x4c\x2a\xe6\x71\x2d\x98\x5b\x34\x1e\xb6\x14\xd6\x1d\x73\x75\xdc\x86\x11\x24\x21\xce\x03\x61\xe1\xb9\x12\xd9\x97\x15\x5b\x4c\xe6\x96\x35\xe5\x3c\x10\x98\x32\x01\x35\xbf\x6d\xd5\x98\xd8\xf0\xe2\xe7\x87\xbc\x58\x12\x2f\xd2\x11\x55\xa8\x89\x56\x91\x9d\xad\xc0\x85\xe7\x20\xe1\x8a\x47\x6e\x53\xf9\x5f\x61\x48\xea\xbc\x03\xe8\x54\xf9\x85\x56\x3d\xea\xbc\x73\xd2\xf5\x13\x77\x4a\x26\xd8\x11\x40\x60\xc8\x06\x2f\xb1\x60\x42\x31\x16\x7a\x87\x88\xb2\x69\xc6\xfb\x88\xd2\x34\x7f\x0b\xb1\x8c\x12\xb6\x84\x92\xfb\xa9\xfa\xe9\x15\x82\xc0\x8f\xac\x35\xd9\x9c\x30\x25\x7e\x23\x98\x3d\x2c\x62\x8c\x56\xed\xdc\x05\xca\xea\x10\xb3\xa6\xf9\x2d\xc4\x9a\x6b\xc5\x10\x64\x61\x1c\x2c\x95\xc0\x42\x4c\x1a\x6f\x89\xb4\x68\xdd\x0a\x2c\x39\x0e\x36\x96\xb0\xc4\x54\xb5\x92\x66\x0b\x63\x65\x79\x6c\x3b\x0b\x5d\x75\x70\x34\xdd\x31\x82\xcc\xb6\x5b\x48\x3c\xd8\xcc\xb6\xb0\xb6\x63\xe8\x86\xd6\x96\x87\x18\x9b\x66\x3b\x87\xdf\x99\xd4\x53\xae\x7c\xe1\x9a\x66\x1e\x19\xb6\x61\x2d\xfd\xe5\xe6\x64\xbe\xdf\xf3\xaa\xd0\xd5\x0a\x9a\x09\x62\x35\xad\x05\xe8\x09\xaa\xce\xa5\xa1\xb7\xc0\xb2\xe4\x8b\x4e\xac\x03\x85\x7b\xd1\xf7\xcb\x29\x87\x61\xe1\x94\xfc\x67\x85\x45\x70\x59\x88\xd9\xd7\xdb\xcc\x21\x7f\x8b\x55\x50\x10\xcc\x0a\x8b\x9e\x4b\xaa\x0d\x2e\xc7\x2d\x97\x14\xc4\x27\xba\x9b\x65\xa1\x68\x1a\x11\x56\x4d\x23\x86\x18\x33\xc1\x39\xe9\xfa\x02\x98\x6c\x1a\x63\x2e\x62\xb9\x9a\xa5\x03\xa5\x81\x4a\x83\x5b\xfd\xf0\xc8\x18\x90\x5f\x97\x27\x83\x62\x96\x2d\x84\xe1\x1b\x83\x2c\xaf\x06\xb3\x6c\x20\xb3\x4a\x2c\x44\x61\x70\xf2\x51\x86\x5b\xfa\xf2\x44\xaf\xae\xcf\x90\xe8\x51\xa0\x07\x12\xb3\x5e\x1e\xb2\x89\x6d\xcb\x69\x90\x75\x1a\x47\x19\x01\xcc\x26\xee\xf4\x57\x7e\x00\x6d\xd4\xaa\x76\x6f\x6c\x8f\x87\x3f\x22\xe1\xc4\xc4\x53\x8a\xdd\xfd\x37\x61\xa5\x1a\x26\x42\xa9\x6e\x9f\xd1\x6f\x05\xd4\x94\x71\xd8\x12\x9d\xd3\x0e\x2d\x8d\x12\x11\xf9\xa8\x28\xf2\x82\x4d\x0c\x7a\xfe\x4d\x2e\xce\xb4\x3b\x03\x46\xbc\x5a\x1b\xca\xc9\x4d\xe4\xc2\x00\x63\x2e\xaf\xf4\xdf\x0f\xf9\x49\x56\x19\x60\x88\x6f\x06\x18\x8b\x4a\xfd\x11\x06\x18\x69\xa5\xfe\xd0\xe3\x4a\x66\x75\x49\xbf\xf9\xdc\x00\x63\x9d\xaa\x97\x75\x21\x62\x49\xfe\xbb\x01\x46\x31\xcb\xe6\xf9\x8a\x1e\xf2\x3a\xa3\x31\x4a\x6f\x18\x60\x54\x72\x25\x68\x70\x95\xbf\x96\x0b\x59\xe9\xc7\xa3\xef\xeb\x3c\x13\x59\x25\x67\xa9\x7a\x3f\x96\xdf\xc5\x5c\x3f\xe5\xc5\x6a\x56\xe9\xc7\x62\xa6\xb6\x48\x2b\xe5\xd7\xaa\xe9\xdd\xd6\x8a\x9d\xac\x1b\x60\x6c\x36\x39\x9d\x88\xa9\x65\x30\x3e\x30\xac\xcc\x32\xfc\x81\x61\x55\x3c\xa8\x96\x45\x7e\x3d\x28\x9c\x6c\xb6\x12\xb8\x19\xac\xe9\x64\xc0\x5b\x74\xa1\xd8\x10\xf4\x63\xc7\x65\x9a\xa4\x7d\x1c\x01\x29\xc4\x30\x23\x95\x02\x4b\x7c\x4f\xfa\x65\xc6\x7f\x0a\x5f\xdb\x7a\x24\xe7\x74\x46\x47\x5d\xaa\xa3\x2e\xd5\x51\x2b\x7f\x46\x29\xa2\xcc\x96\xe0\x86\x39\xcf\x2d\xbc\x81\x1a\x33\x48\x70\x36\x49\xd1\x25\xc3\x90\x8c\x96\x13\x69\xd7\xb6\x37\xdd\xf1\xdc\xc6\xed\x75\x4e\x8a\x73\xc6\x72\xcb\xe3\xa3\x1b\x0e\x69\x88\xb3\xce\xec\x29\xd7\xb0\xe0\x4a\x72\x06\x42\x3b\x01\x5d\xe7\x0b\x4c\x83\x99\x76\x01\x5c\xe2\x41\x8c\x95\x2b\xea\x41\xbe\xa3\x56\xce\xed\x1b\xcb\xd3\x0e\xa6\xd6\xe7\x84\x76\x4a\xce\x8c\xf7\x10\xf5\xad\x39\x12\x62\x74\xc3\x3a\x72\xfd\x7b\xe8\xde\x2a\xd9\x2e\xc8\xe6\x65\x9d\xcd\x9b\x4d\x52\x8b\x8c\x14\xa3\x19\x89\x9f\xec\x74\x33\xc8\xf5\xda\x0f\xab\x88\xc5\x4d\x53\xb4\x16\xb0\x6a\x9a\x0a\x91\x89\x2d\x0b\x18\x87\x4f\x9b\xe6\xa9\xd6\x5a\xfb\x6a\x44\xa1\x2c\x20\x79\x1d\x79\xe8\x46\x75\xe8\x46\x2d\x1a\x53\xdf\xf5\x67\x93\x94\x60\xef\x78\xae\xe9\x6d\x03\xeb\x2c\x63\xd6\x34\xc3\xd9\xc6\xf4\x0f\x3a\x5a\xd1\xb9\x47\xa4\x6c\x85\x0a\xb6\x68\x08\x2e\x27\xd9\xce\xcd\x14\x48\xda\xec\xac\x69\x5c\xee\xab\x66\x25\x85\x20\x94\xcb\x80\x98\x47\xac\x87\x91\x42\x89\x1e\xa4\xb6\xcd\xfd\xad\x46\x8b\xf8\x61\x39\xb9\xb1\xf3\x29\x10\x7d\x91\x50\x5e\xb1\x0e\xe9\x9d\xe5\xa4\x9e\xf2\xdd\xd2\x77\x39\x14\x4a\x4b\x07\x5a\x4b\xba\x88\xa9\xd6\x30\x39\x7a\x50\x6b\x96\xaa\xd5\xb9\xd4\xea\x5c\xf2\x8d\x8b\x4c\x7d\x16\x96\xb4\xfe\x9d\x21\xa5\x3a\xba\x21\x96\xa4\x9d\x1d\x61\x59\x7a\x67\x78\x66\x9a\x4c\x3d\x91\x31\xd7\x6a\x97\x98\x78\x92\x2a\x28\xf4\x3b\xc4\x33\xcd\x55\x01\x91\xd4\x26\x57\xa0\x44\xef\x56\xa3\x33\xdb\x72\xae\x70\xa6\x5c\x06\xe2\x34\xad\xeb\x6e\x85\x23\xee\xab\x30\xe1\x88\x17\x6f\x14\x0e\xbd\x1a\xdb\xb2\xfd\x24\x5b\xaf\x94\xec\x7d\xc0\x99\xb3\x2e\xf2\x2a\xa7\x70\x0b\xbe\xb5\x76\xc2\xe3\xf0\x0e\xc7\x2e\x7c\xc5\x7d\xf8\x0d\xed\x03\x78\x82\x63\x0f\xde\xa0\xed\x89\x03\xf8\x81\xf4\xf7\x0b\x0e\x5d\xf8\x17\x1e\xc3\x1f\x38\xf4\xe0\x4f\xf4\xe0\x77\xf4\x5c\x17\xfe\xc2\x9f\xad\xe6\xbf\x10\xeb\x59\x31\xab\xf2\xc2\x27\xf7\x73\x51\xe4\xf5\x7a\xab\x09\xba\x26\xf9\x43\xf8\x7b\x50\x8a\x38\xcf\xe6\xb3\xe2\xe6\x4d\xdf\xe8\x42\xd2\x2a\xa1\x37\xf7\xe6\x0e\x8c\x7b\x5d\x6a\xf8\x6d\xd0\xb3\xd8\x2c\xcb\xab\xa5\x28\x30\x83\x99\xf3\xfe\xfc\xe3\xd9\xeb\xcf\x1f\xdf\xa1\xdb\xbf\xbc\x3e\xff\xf3\x0c\xbd\xfe\xf5\xd5\xd1\xc9\x29\x8e\xfb\xd7\xe3\xd3\xf3\xf3\xf7\xb8\xd7\xbf\xff\xeb\xe5\xe9\x31\xcd\xdf\xbf\xdb\xa2\x80\x3c\xbd\xdb\x76\xf4\xc7\xd1\x19\x3e\xbb\xdb\xa6\xa0\x1f\xdc\x6d\xd3\x4b\x3c\x87\x99\x73\xf4\xf1\xd5\xe9\xc9\x6b\x3c\x84\x99\xa3\x6d\x03\xf6\xa9\x17\xad\x02\x95\x3e\x24\x61\xc1\x9f\xb7\x20\x71\x56\x2c\xea\x95\xc8\x2a\xe2\x3c\x49\xee\x55\x42\xac\x66\xe4\x97\x5f\x44\x5c\x6d\xa2\xe6\x32\xda\x02\xd3\x92\xa5\x74\x96\xb3\xf2\xfc\x3a\x7b\x57\xe4\x6b\x51\x54\x37\x2c\xe3\x91\x56\x19\x4c\x60\x39\xc9\xa6\xdc\xa7\x60\x78\xe0\xde\xfa\x0f\x27\xcb\x2e\x8d\x50\x6d\xe6\xc8\x49\x45\xce\x65\x37\xab\x8f\xaf\x59\x86\xc6\xeb\xa3\x57\x27\x6f\x5f\x9e\x7e\x7e\x77\xfa\xf2\xd5\xd1\x85\xc1\xc9\x7f\x14\xe0\xc2\x11\x8c\x21\x23\xe5\xf3\x0e\xdd\x86\xa2\xc1\x49\x36\xc5\x77\xa0\xe6\x28\x02\x9d\x9c\xbd\xf9\xfc\xf6\xfc\xf5\xd1\x66\xca\xf3\x6e\xca\xd7\xad\x29\x5f\xf5\x94\xa3\xbf\xde\x9d\x9f\x1d\x9d\x7d\x38\x79\x79\xfa\xf9\xe5\x07\x9a\x43\xde\x11\x8f\xfe\xa5\x5c\x21\xb0\x8f\xc0\x6d\x67\x53\x8b\x37\xdd\xc6\xe0\x37\x02\x47\xa3\x9e\xa8\x07\x6f\xca\x7d\x5a\xd0\x3e\xda\x1e\x62\x33\xea\x65\x6e\x28\x22\x5b\xf8\x82\x73\xde\x22\x30\xf9\x0d\x9e\x4c\x5b\xbc\x5f\x9e\xbd\x39\x7a\x6c\x6d\xdb\xbb\xbb\xb8\xb7\x81\xfc\xa6\x5b\xfc\xc7\x2f\x17\x77\x1b\x11\xbd\x41\x9b\xfd\xb8\x8b\x80\xaf\x33\x66\x90\x59\xc6\x20\x9e\x65\xe4\x39\x5d\x8a\xc1\x0f\x51\xe4\x06\x88\x0d\x7a\x6f\xe0\x47\x8b\xde\xd1\xfb\xf7\xe7\xef\xd5\x11\x30\x81\x88\xc3\xa1\x68\x1a\x0f\x11\x45\xd3\x90\x36\x11\x11\x23\x45\xf0\x2f\x64\x5f\xa8\x8f\x47\xc7\x7e\xbe\xb5\xc8\x35\x01\xd5\x30\xbf\x68\x78\xaf\xde\xff\xd7\xbb\x0f\xe7\xff\x13\xbc\x3f\x70\xc8\xa8\x75\xb8\x6c\x9a\x8e\x35\x87\x1d\x6b\x2e\x39\x08\xd3\x1c\xfe\xa1\xf2\x03\xb4\x86\x11\x17\x37\xeb\x2a\x1f\xd4\xd9\xec\x6a\x26\xd3\xd9\x65\x2a\x0c\x58\xf2\xc7\x71\xf8\x43\xe3\xf0\xf6\xfc\xf5\xc7\xd3\xf3\x7b\x8c\x72\xd8\x51\xee\xcf\x2d\x46\xf9\x53\x4f\x78\x77\xfe\xe7\xe7\x77\xef\x8f\x5e\x9d\x5c\x9c\x9c\x9f\x3d\xc2\x8e\xbf\x6f\x4d\xf9\x5d\x4f\x39\x3e\x7f\xff\xb6\xe5\xa9\x07\xf2\x25\xa2\xbf\x50\x6c\x9f\x44\xeb\xc0\xb6\xe3\x36\xf8\xfe\x05\xc5\x2d\xcc\x9c\xd5\xec\x3b\x3e\x14\xaa\xef\x6c\x23\xce\x1f\x9c\xb4\xe2\x6a\xa8\xcc\xfe\xd7\xa1\x0b\x3d\x54\xfb\x7d\x0f\x34\x06\x1e\xba\xee\x81\x77\x78\x38\x7e\xba\x7f\xb0\xef\x1e\x1e\x8e\x21\xc3\xb7\xb3\x6a\xd9\x8e\x67\x7c\x57\x98\x63\xf7\xf0\xc0\x7b\xea\x3d\xa2\x26\x56\xec\xde\x58\xfe\x98\x3e\x78\xbe\xf7\xfc\xf9\x33\xf7\xf9\x2e\xf3\xdc\x83\xbd\x83\x7d\xef\xf9\x78\x7f\xf7\xce\xbc\xc6\xe5\x16\xeb\x46\xdd\xef\xd9\xe8\x8a\xad\x3c\xf3\xbd\xe4\x31\xba\x90\xe0\x64\x0a\x69\x6b\x93\xbe\x29\x6f\x4e\xb4\x01\xa9\xd8\x9c\xa0\xb7\x4f\xf1\xa8\xf0\xdf\x41\x8e\x73\x26\xc8\x61\xfb\x83\xcb\x84\x2d\x4d\x73\xe9\x2c\x44\xf5\x5e\xad\xfb\xc7\x2c\xad\x45\xa9\xcd\x7b\x85\x0f\x3a\x54\x80\xf9\x51\x66\xd5\xde\xf8\x65\x51\xcc\x6e\x58\xbe\x8b\x63\xce\x83\x3c\x2c\x03\x5e\xa3\xb7\xe7\xb9\x07\xe3\xdd\x6a\x52\x4e\x2d\x56\x4d\x4a\xcb\x9b\x86\x61\xe8\x79\x1c\xea\x10\x0f\x85\xf7\x34\x62\xc5\x3f\x00\x3a\xe6\x1c\x08\x06\x16\x24\xfa\x1a\x0e\x16\x4a\xfa\x59\xa2\x1d\xc7\x7a\xc7\x13\xde\x3e\x87\xd2\xc2\x31\x0f\x4a\xcc\x47\xe3\x3e\xb8\x54\x3b\xd2\x64\xfc\xed\xa6\xda\xde\xcd\x56\x23\x61\x7e\xd0\x23\x3e\x7e\xee\xed\x1f\xec\x1f\x1e\x3c\x3b\xf0\xdc\x67\x4f\x9f\xed\xb2\x3d\xcf\x24\x0c\xb8\xe5\xb9\x87\x87\x4f\x3d\xef\xd9\xf8\xe0\xe0\xe0\xd9\xae\xc6\xc5\xda\x1f\x1f\xee\x1f\x3e\x3b\x18\x1f\xea\x96\xf1\xd4\xf2\x9e\x1d\x1c\x1c\x8c\x3d\xfd\xbe\xd7\xee\x7e\x7f\xfa\xe2\x85\xf7\x8c\xeb\x97\xa7\xd3\x17\x2f\x9e\x73\x8b\x1e\x9f\x4d\x7b\x7a\xdc\xc5\xe9\x80\x3b\x71\xbe\xbe\x61\x15\x85\xf7\x8f\x6c\xf5\x40\x6f\xf5\x40\x6f\x55\xc9\x95\xb7\xff\x2b\xcd\xa0\xd2\x49\xa5\xf6\xdc\xda\x6d\x66\x8c\x03\x2d\x1b\xd6\xa6\xc9\x92\x49\x69\x59\x53\x6c\xc1\x07\xda\x83\x4a\x26\xb6\x5d\x4e\x41\x90\x57\x9d\x9b\xa6\x20\x6d\x8d\xef\x27\x37\xb6\x98\x42\x42\x47\xb2\x62\xf9\xa8\xe6\xbb\x35\x57\x3e\x16\x35\x05\x89\xf6\xb0\xa0\xb4\x6d\xae\x13\x56\x25\x4f\x70\x22\xfb\xac\xa4\x0e\x3f\x6c\xaf\x9d\xe2\xd2\x14\x9d\xb3\xe1\x20\x6d\xbc\xd1\x8b\x97\xca\x9b\x4c\xee\x7b\x93\xca\x55\xbc\x09\xc9\x53\xa4\xb1\x76\xd9\x3b\x68\xa9\x23\x50\x42\xea\xc4\x98\x40\x7a\x7b\xcb\x38\xbc\xda\x16\xf2\x3e\x5a\x12\x77\xc2\xcf\x3b\x82\xd3\xc5\xff\x24\x3e\x3b\x2f\x21\xc6\x6c\xf4\xb2\xd1\xe9\x03\x81\x7d\x02\x3e\x48\x6c\x3b\xe0\x39\x8a\x49\x32\xdd\x79\x09\xb5\x7a\xa0\x81\x50\x60\xbc\x9b\x5b\xf5\x6e\x0a\x12\xd3\xdd\xdc\x2a\x76\x5e\xee\xbe\xb4\xc8\xeb\x60\x72\x54\x29\xe1\x2e\x68\x20\xb7\xe2\xdd\x1a\x68\x1a\xca\x9d\xaa\x13\xeb\xd2\x34\x45\x9f\xbe\x2a\xef\x84\xcc\xd9\x83\x08\x4f\xe5\x99\x86\x58\xf0\x1c\xab\xb0\x88\x3c\xdf\xf6\x74\x18\xa6\xa9\x9b\xa3\x1b\x54\xa1\x54\xf9\x69\x52\x00\x13\x39\x1d\x62\x36\x91\x53\xfe\x93\x10\x97\xd3\x90\x5e\xf4\x34\xed\x58\xb7\x48\xe4\x9b\x45\x8b\xcd\xa2\x5d\x02\x41\x12\x58\xda\xbd\x98\x54\x53\x1b\x25\x48\xa4\xa7\x17\xd9\xa4\x22\x60\x2e\xd0\x1b\xca\xdd\xc2\x52\x03\xa8\x59\x07\x7b\x43\x32\xdb\xb4\xbf\xee\x5e\x25\x10\xdd\x99\xf3\xe0\xf6\xbe\x5e\xeb\x23\x58\xbd\xdd\x74\x93\xe4\x85\x6b\xb8\x82\x4b\x38\x87\x0b\x78\x0f\x2f\xe1\x08\x5e\xc3\x67\xf8\x0e\xc7\x28\x9d\x12\x31\x77\x4a\xb5\x25\x38\x41\xe9\xc4\x70\x8a\xb9\x13\xeb\x7b\xb4\x13\xd3\x3c\x51\x18\x9c\x9a\xe6\x29\x05\x56\x5d\x64\xa5\xd5\xa4\x74\x4a\xd3\xcc\xe9\x0f\x3b\x89\x86\xa7\x4d\x43\x83\x87\x48\x23\xfd\x53\x1e\x9d\x98\xa6\x8b\x48\x6d\x4d\x33\x3c\x8d\xdc\xdd\x63\xff\x78\xe4\xfa\xee\xc8\xd5\xbc\x7a\xd5\x6a\xdb\x63\x0e\x97\x78\xa5\x73\xed\x31\x4a\x47\xd8\xb9\x23\xe0\x18\x6b\x2b\xb6\x3c\x48\x9a\x86\x25\x78\x06\x31\x56\x4c\x3a\xa4\x72\xed\x8a\xe5\xea\x01\x8e\xf1\x78\x74\xd3\xb8\x1c\x96\xe8\x06\xa7\x93\xe5\x14\x91\x9d\x4c\x96\x53\x8a\xe7\x82\x65\x1b\x94\x53\x7b\xd8\x37\x9b\x66\x6c\xdb\xe0\x86\xc7\xfc\x52\x6b\x06\x8f\xc3\x02\x87\xee\x46\xc8\x8e\xf0\xa4\x63\xe8\xcf\x78\xda\x3d\x52\x10\x79\x6c\xe1\x18\xd6\x48\xe1\x1d\xa3\x4d\x5a\x1e\xe7\xb0\x0e\x3d\xd3\x64\xa7\x28\xd8\x29\xac\x21\xe1\x70\x82\x82\x9d\xe8\xc7\xad\xf9\x1b\xa8\x1c\x5e\xe2\x67\x38\xc7\x93\xfe\xaa\xe0\x33\x87\x0b\x3c\xef\xc2\xae\xcf\xe1\x45\x70\x3e\xb9\x20\xb5\xe2\xf2\xe0\x3b\x9e\x76\x12\x04\xdf\x7b\x3e\x77\x39\xbc\x56\x74\x86\xd3\x89\x37\x0d\x31\x19\x8d\x4d\xf3\xb5\x65\x05\xf3\x7c\xb0\x46\x97\x24\x91\x9d\xc2\x39\x7c\x86\x0b\x0e\x6e\x98\x46\xec\x3d\x9e\xd3\xf0\xcf\x43\xbc\x30\x4d\xf6\x1e\xdf\xef\x26\x16\x3b\x9f\x78\x8a\x28\x5c\xed\xea\xfd\xe8\xb5\xda\x4e\xc4\xd6\xa1\x4a\x4a\xaf\x31\xb1\x3d\x0e\xf3\xcd\xde\xae\x71\xde\x6d\x68\x83\xb1\x5a\x6d\x0e\xe7\x70\x4d\xab\x79\x88\x29\xcd\xb5\x6d\x28\xd8\x1c\xae\xc3\xcf\xd1\x77\xff\x14\xae\x21\xe1\x9c\xfb\x14\xf8\xae\x4d\x93\xa5\xb8\x46\x05\xba\xdf\xdd\x5d\xe0\xe1\xb5\x69\xce\xb7\xb7\x5b\xb0\x73\x98\xc3\x05\x21\x61\xb7\x4b\xdc\xc3\xa0\xdf\xaf\x17\x2a\x04\x2c\x4b\x4d\xba\x68\x11\xb8\x50\x08\x6c\xa1\xcd\x7d\xd2\xa4\xdd\xd0\x73\x54\xd9\xcd\xcb\xc9\x92\x08\xbf\x86\xd4\x34\x89\x60\x51\x7b\x12\x27\x93\x97\x44\x29\x9f\x9d\xe3\x84\x9e\xa7\x70\x81\x1e\x0f\xae\x97\x32\x15\x8c\xbd\xb4\xac\x17\x47\x5d\x52\xe4\x5c\x27\x4c\x8f\x49\x91\x2f\x70\xd3\x06\x97\x4a\x12\x2e\x3b\x09\xa6\xa0\x3c\x41\x3c\xd3\x7a\x62\x89\x1e\x1c\x23\x0d\x09\x8e\x95\xe2\x3e\x56\x8a\x5b\x31\xf1\x47\x76\x05\xb5\xc5\xae\x1c\x81\x4b\x2b\x56\x69\x44\xcb\x83\x12\x16\x6d\x26\x99\x3a\x62\xb8\x72\x0a\xb4\x16\x9d\x5a\xbc\x52\xba\xfc\x61\x88\x87\xa3\xbf\x99\x1d\x71\x97\x4d\xbe\x5f\xe6\x53\xce\x3e\x5d\x4f\x3e\x5d\x3b\xd3\xdd\x27\x7c\x24\x21\xa3\xde\xc9\xdf\xce\xd4\xe2\x9f\x9c\x27\x23\xa8\x70\xf4\xf7\x27\xa7\x6d\x79\x32\x82\x02\x47\x7f\xdb\x11\x3b\xc9\x12\x99\xc9\xea\xa6\x39\x9b\x9d\x51\xb3\xa4\x61\xe5\xee\x27\x8b\x29\x58\xbc\xf9\xfb\x53\x69\x35\x9f\x4a\xeb\xc9\x68\xf1\xc0\xfb\xba\xaf\xa3\xb0\x8c\x6a\xbf\xee\xaf\x8f\x24\x18\x4f\x3c\x43\x09\x6e\xa1\x2f\x45\x63\xce\x73\xa7\x44\x59\x9e\xcd\xce\x58\xac\xe3\x48\xdf\x0d\xe3\xc8\xf6\x7c\xaf\xbf\xf2\x18\x92\x16\x8a\x31\xee\x01\x09\xd8\x38\x7c\xda\x72\x75\x16\x0f\x8d\xef\x06\x22\xab\xb0\xba\x77\xad\x15\x79\xcf\x7c\xe3\x92\x3c\xef\x68\xec\x3f\x87\xc4\x34\x93\x21\xa6\x91\xf0\xb3\x5b\x4e\x6f\x2c\xc5\x04\xb6\xd7\xc8\x34\xb2\xfd\x7b\x05\x86\xeb\x50\x0b\x87\x7a\x88\xf1\x3d\x75\x19\x43\xca\x83\x2f\xfa\x8a\xd2\x50\x4e\xbc\x61\xb1\x24\x32\x06\x97\xb3\x52\x0c\x0c\x2b\xf1\x0d\x83\x93\x7f\xdf\xe6\x71\x6b\x0e\xb4\x71\xda\xef\x6d\xee\xc4\x98\xb7\x09\x17\x78\x8b\xae\x3a\xdd\x0f\xce\xec\xb2\xcc\xd3\xba\x12\xca\x07\x44\xf5\xfe\xf0\xc4\xdb\x7b\xb8\xa5\x2c\xef\xdf\x03\x30\xe1\x94\x24\x86\xe2\x16\x3e\x38\xb1\x90\xe9\x23\xd1\x40\x77\x1f\xa2\xe6\x03\xfd\x55\x49\xb4\x31\x57\x73\xf2\xd5\x7a\x56\x88\xf9\x87\x1c\x3f\x38\xf1\x6a\x8d\xdb\x34\xef\x41\xbc\x45\x0f\xa4\x02\xb0\x55\x58\xa1\xe6\xb7\xe9\x9b\x77\x2a\x6f\x8f\x1f\x9c\xf9\xfa\xb1\x9c\x44\xa1\x4a\x3b\x5a\xa3\x54\xf4\x44\xad\xd3\x54\xbb\xe9\x8c\x65\x58\x74\x77\x8b\x1e\xd9\x07\x8d\xe6\xe8\x86\xf3\xdd\x1b\xc8\x90\xc2\x23\xed\xc3\x65\x3b\x9e\x8b\xe8\x06\x99\x92\x2e\x41\x32\xda\x82\x73\x43\xa1\xa2\x4c\xb7\x25\xc7\x5c\x5e\xc9\xb9\x98\xff\x76\x83\xea\xf9\x57\x3b\xdb\x83\x57\xf7\x77\x06\xef\xe0\x2b\xdf\x02\xa1\xd2\xee\x62\x21\x8a\x0e\x96\x6a\xf8\x15\xc0\xfd\x47\x00\xba\xe0\x29\x80\xe2\x5b\x3d\x4b\x89\x4e\xe2\xdb\xaf\xa6\x3f\x05\xd2\x6a\x8f\x53\x3b\x49\xf3\xbc\xf8\xe7\x47\xbc\xa7\x26\x2d\x0a\x31\xab\x44\xf1\x61\x39\xcb\x90\xa2\xc1\x5f\x2d\xfc\xec\x91\x23\x0e\xdd\x7b\x10\xce\x8b\x23\xda\x82\x62\x97\x45\x25\x7e\x05\xeb\x80\xac\x08\xb2\xec\x91\x7d\x70\x1d\xf9\x67\x04\x58\x96\xc7\xa4\x87\xc4\xc3\x2d\x0d\x87\x9a\x63\xf4\xa8\x96\xfc\xd8\x3e\xff\x7a\xb8\x69\x6e\xb1\x4e\xa8\xdb\x3a\xbe\x1a\x6b\x58\x67\xb3\xb3\x47\xe6\xab\xa1\x65\x3b\x42\x2c\x66\x95\xbc\x12\xd8\xbe\x3c\x42\x70\x3d\xfc\x85\xab\x27\xfc\xb7\x28\xf2\xff\x09\x27\x17\x5b\xfe\x9f\xb8\x53\x9a\x91\x8a\xb2\x6c\x8f\x23\xfd\xe5\x71\x3c\x7f\xe4\x38\xf4\x82\xdd\xf4\xed\xb3\x48\x7f\x7d\x16\x87\xca\xde\xfe\xef\x87\xa1\x6e\x8e\xf0\x83\x53\xd6\x97\xf7\x40\xdd\x8d\x18\x14\x8c\x04\x4b\x47\xd5\x6a\xbd\x55\x62\x88\x5b\xbc\x9e\xa9\x5a\x9e\x61\xd2\x34\xc3\xec\xae\xfe\x54\x8e\x23\x19\xcd\xe1\xa6\xc0\x8a\x14\x98\x9d\x41\xe9\xac\xd3\xba\x64\x82\x07\xca\xaa\xa0\x3a\x41\x50\x39\xea\xd1\x0d\x2c\xb1\x74\x62\x58\xa0\x68\x55\x48\xda\x34\x43\x7d\xd1\x3a\x5c\x36\xcd\x70\xd1\x01\x5b\x46\xac\x85\x27\xb8\xaf\xd7\x5c\x44\xa5\xdf\xad\x3b\x5c\x6a\x57\x76\xab\xba\x60\x40\xcf\x0f\x67\xd1\xc0\xa8\xf4\xf7\x10\xbf\x46\xb6\xeb\xbb\xca\xd6\xa7\x58\xb1\x94\x2b\x3f\x56\xdd\x49\x2f\x7b\xbf\x2e\xc1\xd4\x8e\xb5\x1b\xc0\x6a\x74\xc3\x84\x47\x2c\x41\x3b\x81\x1c\x97\xdc\x67\x31\xa6\x90\xe3\x82\xac\x41\x21\xae\x44\x41\xb6\x0a\x32\x4c\xd4\x05\x6f\xbe\xb9\x03\xda\xea\xbe\xdd\x0a\x6a\x58\x8d\x2c\xe9\x6f\xad\xf9\x0b\x96\xf5\x77\xfb\x9c\x47\x89\x9f\x41\x82\x19\xba\x81\x0c\xb3\x20\xd3\x81\xcf\x72\x92\x4d\x87\xb8\x20\xad\xf9\xb3\x46\x7a\x7b\x41\x2f\x9b\xcb\x04\x0a\x7d\x73\x24\xaf\x78\x01\x0b\xcc\x41\x11\x40\x38\x25\xe1\xc5\xe4\x06\xbe\xad\x52\x15\x9d\xdf\xdb\xdd\x54\xeb\x9b\xe9\x49\xd1\xba\xb8\xd4\x94\xe1\x99\xed\x05\x32\x4c\xf4\xf5\xc8\x52\x5d\xb1\xbe\x58\xa8\xd0\x4b\x17\x5a\xc9\xa0\x30\xcd\x21\x75\x14\x53\x9a\x3c\xc5\x8c\x07\xb6\x4d\x4f\xb0\x9c\xc8\xa9\x85\x67\xb7\xf4\x6b\x23\xcd\x52\x77\x19\x14\x2a\xd3\x51\x04\xcb\x3e\x52\xb6\xed\xb8\xd7\xf8\xea\x94\x4e\x98\x80\x25\xc4\xdc\x57\x87\xa8\x4f\xcc\xf3\x3d\xd8\xba\xcc\x00\xa1\x14\xe1\x2a\x9f\xd7\x29\x09\xcb\x2a\x9f\x3f\xc2\xe1\xfa\xd6\x5c\xd5\x20\x6e\xcc\x9e\x77\x97\xb7\x87\xd2\x89\x9b\x66\x28\x9c\xb2\x69\x04\x89\xf6\x50\x17\x2e\x44\x1b\x06\xf7\xa9\xa9\x69\xa4\xea\x95\xdb\xbd\x92\xfb\xec\x10\xf1\xcf\x88\x15\x4a\x44\x94\xed\x86\x0a\x5f\x31\x09\x02\x5c\xd8\xe3\xaa\xa9\x80\xca\x29\x77\xb1\xe0\xfe\xa6\xeb\x4f\x0e\x52\x0b\x28\xab\x1c\x75\x51\xcb\x04\xd7\x36\x21\x23\x6d\x25\xe6\xa8\x9e\xfe\xa9\xef\xa0\xce\x5a\xfb\xbb\xda\x58\x92\xf4\x91\xfb\x31\x7f\x8c\x32\x1d\x5d\x20\xa7\x78\xb3\x95\xfa\xf1\xa3\x52\x9f\xff\x5a\xea\xf3\x87\x52\xdf\xed\xa9\x15\xfb\x1a\x55\x7c\xa8\xab\x40\x46\x37\x90\xa8\x70\x36\xed\xc5\xbe\x6e\x9a\x61\xa9\xc5\x9e\xb4\x4b\x7a\x77\x9d\xbc\x93\xf2\x44\x4b\x79\xba\x25\xe5\xf4\x4c\x6e\xa0\x1a\x48\xfd\x91\xf4\xdd\xdd\x5c\x89\x75\x8d\x15\xab\x39\x29\x36\x56\x92\x28\x27\xbd\x58\xe7\x58\xdb\x6d\xde\x2c\x0f\xdd\x88\x95\x58\x43\x81\x29\xf7\x59\x8e\x76\x0e\x05\x26\x1c\x8a\x8d\xcc\x06\xb9\x6d\x07\xc5\x46\x9c\xb7\xba\xda\x9b\xb9\xa4\x0b\x77\x32\x4c\xbb\x47\x37\xcc\xed\x4c\xd5\xdd\xa5\x40\xee\x69\x82\x05\x64\x98\xd3\xea\x6e\x90\x05\x3c\x47\x96\x4c\x6c\x3b\x9b\x62\x32\xc9\xa6\x56\x4a\x7f\x72\x3e\x3a\x6b\x5c\xa0\x86\x1d\x3c\xeb\xce\x35\x37\x4d\x96\xf4\x21\x57\xce\xc1\xb2\x4a\x0e\x24\x1f\x09\x94\x8a\x57\xfa\x3a\x00\x52\xf3\xdb\x27\xad\xcf\x59\x65\x3d\xf4\x49\x4b\x2c\x34\xd1\xfb\x0c\xaa\x18\xaa\xf4\xbd\x69\x7a\x43\xa4\x77\x57\xff\x30\x9d\x7f\xdb\x03\xa3\xcb\x39\x1b\x2a\x05\x0f\x62\xa8\x87\xb7\x59\x58\x4e\xc2\x73\xdf\xf3\xab\x50\xf6\x5e\x1f\x64\x58\xed\xde\x58\x24\x10\x72\x52\xb5\x5a\x23\xa8\x5a\x77\xaf\x52\xee\x5e\x46\xee\x9e\x4e\x63\x4a\x52\x0b\x95\x0a\xb4\xda\x3e\x0a\xb4\xfa\x5b\x4b\xd3\x2c\xc8\x05\x0a\x89\xb2\xe4\x5b\x0a\xcb\xe3\xa0\xcc\x9c\x2a\x7b\x78\x4c\xfc\x1f\x11\x15\xa6\x2b\x91\x44\xd3\xf4\xf9\xe3\xa7\x9c\x9b\xe6\x47\x56\xc1\xbf\xff\x2d\xac\xde\xd3\xba\x53\x60\xec\xc2\x73\xf0\x9e\xea\xca\xa7\xcc\xff\xca\xa1\xa2\x75\xd5\xa9\x3c\x24\xf9\x1d\x85\xa3\x6e\x75\x2e\xe0\x02\xbc\x67\x5b\xf4\xe4\x51\xd6\xca\xbc\xe1\x09\xc3\x52\xb5\x33\x2d\x2b\x67\xa4\x65\x32\xa5\x64\x4c\x93\xd9\x17\xba\x68\xe6\x82\x66\x94\xbb\xea\x1e\xc8\xf5\x3d\x52\x4a\x99\x3a\xff\xf2\x5b\x3d\x2b\xc4\xfb\x3c\xaf\x88\x01\xbe\x15\xd5\x63\xce\xfa\x03\x3b\x4f\x22\x58\x3a\x25\x45\x7a\xaa\x90\xea\x9d\xb5\x0f\x8b\x96\x5a\x86\xeb\x3c\xd5\xc1\x1e\xb1\x05\xd9\x65\x92\xcc\x64\x4b\xf4\xf4\x38\x32\xd9\xae\x0a\xeb\x69\x80\xea\x8f\xdc\x91\xeb\x27\x51\xa9\x10\x0c\x94\x7d\x55\xa9\x7f\xc2\x8b\x11\xe7\xba\x0a\x60\x8a\xe8\x8d\xdc\x88\x4e\x91\x25\x1c\x58\x57\xc6\x63\xc5\x7c\x67\x8c\xaa\x8a\x31\xd3\x35\x52\xb0\x0d\x20\xd3\x86\x9a\xc5\x96\xc7\x47\x63\x6e\x33\x37\x8c\x9b\x26\xde\x19\xd3\x30\x05\x31\x43\x4d\x4e\x9f\x91\x34\xde\x29\x75\x51\xe6\x39\xdb\xd4\x64\x6f\x2a\x2c\x85\xc1\x2d\x8f\x5b\x31\x07\xd9\x52\x20\xe3\xdc\xef\x9e\x53\xcb\x30\x48\x53\xd3\x79\x28\x43\xa9\xb2\x61\x90\x62\x6c\x2d\x61\x4f\x6d\x3f\x25\x83\x19\xe8\xfa\x57\x09\x64\x69\xf5\xd1\xd6\xda\x01\x7a\xc5\x4a\xa8\x61\x09\x9e\xba\x9c\x63\xb5\x13\xf3\x1e\x8d\x94\x6b\x37\xae\x60\xd2\x89\xf9\x76\xbb\xd2\x89\xd2\x11\x2f\x62\xd3\xb4\xed\x74\x0b\xf9\xd4\xde\x83\x94\x78\xdf\x38\x3c\x3c\x3c\x34\x14\x8f\xb2\xbc\x69\x8c\xfd\xf6\x95\xf3\x9f\x6c\x68\x65\x4d\x33\xb4\xb2\xbe\x10\xd9\x34\x8d\xa7\x06\x62\xd6\x55\x06\xba\xc4\xf4\xec\x23\x93\x20\x1d\x61\xbd\xb3\xc6\x40\x31\x27\x0e\x65\x8b\xbc\xe4\x8e\xf8\xc6\xca\xed\x6a\x85\x61\xae\x66\xd4\x50\xb7\x33\x5c\x0e\x75\xb7\xd7\x6e\x38\xff\x29\xb1\x6e\xe7\x2c\x2d\xdc\x87\x94\xfe\xe4\xe8\xdd\xf6\x81\x4d\xb7\xa4\x07\x5f\x5b\x33\xae\x60\x90\x15\xaf\xd3\xff\xc9\x4f\x6d\xeb\x80\xba\x04\xea\x4a\xa7\x50\x35\x57\x9f\xe3\xa5\x13\xc3\x05\x92\x1d\x3b\xb8\x63\xc7\x78\x97\x39\x3d\x37\xcd\x0b\x9d\x41\x32\xcd\x8b\xad\xcc\xe9\xf0\x92\x0c\xa7\xf6\x00\xce\x4d\x73\xa8\x47\x0c\x2f\x9a\xe6\x82\x7e\xf4\xdb\x79\x5f\x5f\x21\xda\xf8\x5f\x79\x27\xbb\x78\xe9\x94\x40\x90\x23\x5d\x6b\xe1\xea\xfa\x15\x97\xfb\xdb\xf5\x18\x1c\x44\x5b\x92\x56\xb1\x4b\x15\xc9\x58\x15\x13\x3a\x61\xda\x43\x49\x37\xb9\xb3\x05\x5e\xf4\x8f\x8a\xc7\x56\x78\x0e\xe7\x78\x01\x17\xb8\x82\x5c\x99\x15\xe5\xe4\x91\x49\x49\xad\x05\xac\x70\x32\x55\xb6\x6a\xb5\x55\x7e\x94\x17\xec\x1a\xcf\xe0\x0a\x5f\x92\xab\x1a\xd8\x76\x1e\xa2\x1b\x6c\x8a\xe4\xd7\x78\x31\xc9\xa7\x3b\x57\x30\x57\x0f\xa3\xab\xc6\x85\x12\x53\xa8\x31\xb7\xca\xa0\x0e\xf3\x80\xc7\x78\xae\xee\x4d\x76\xae\x60\x89\xe7\x93\x52\x0f\x4a\x70\xbe\x1b\x5b\xcb\xdd\x35\xc4\xb8\xde\x8d\xad\x64\xe7\x6a\xf7\xca\x5a\x4d\xea\xa9\x55\x40\x81\x2c\x1e\x5d\xab\x1b\x82\x84\x46\x73\x6b\xbe\xbb\x84\xd5\xa4\xb6\xed\x29\xc6\x3b\xd7\x01\x8d\xc3\xa2\x63\x87\x22\xb2\x2c\xe9\xaf\x7a\x67\x90\x6c\xdb\x0a\xa4\x66\x8b\xb6\x6c\xed\x1f\xaa\xf6\xc1\xbd\xcb\x41\x8f\x94\xfb\xf3\xed\x52\x39\x7d\x51\xa8\x5c\xa4\x0c\x1f\x2a\xf8\xe7\xbd\x82\x07\x11\x91\x41\xa0\xe5\xfc\x4a\xa3\xb2\xa5\x4b\x1e\x0f\xcb\x3e\xb7\xa1\xd8\x83\xfb\xc9\x43\x1e\x91\x65\xf1\xda\x85\xa9\x41\x83\x54\x95\x77\xff\x37\x60\x63\x57\x03\xeb\xcc\x54\x07\x73\xec\x76\x30\x55\x0d\xdf\xa3\x14\xfb\x25\x4c\xef\x17\x30\x3d\xa5\xc3\x75\x9c\xbb\xe5\x36\x3a\xe5\x3a\x95\x95\x2e\x4d\xcf\xd1\xfa\xcb\xe9\x0b\x79\xa0\xa6\xd7\x87\xb5\x3c\x50\x62\x37\xaa\xab\xe2\x21\x4f\x90\x84\x25\x45\x39\x51\x25\xda\x5d\xfc\x0d\x33\x8c\xa3\xa4\xd7\x5b\x7e\x02\xcb\x4d\xf9\x53\x1b\xe6\x14\x98\x93\x27\x07\x35\x16\xb0\xb4\xb1\xe0\x90\x87\xae\x69\x2e\x43\xb7\xe3\xee\xe5\x4e\xde\x34\x39\x24\x38\x6b\xbf\x89\x60\x2e\x14\x3c\x58\x86\x45\x50\x58\x98\xf3\xc4\xc2\xd2\xea\xfb\x0a\xc8\x79\x50\x87\xaa\x7c\xbe\xed\x50\xcb\x17\x9c\x43\xac\x6a\xea\x0d\xdb\xb0\x12\x7e\x5b\x61\x1a\x25\xd6\x5f\xce\xfd\x12\x27\x8b\x82\x44\xeb\x2f\xe7\x41\x59\x12\x8f\xd2\x4d\x66\x72\xeb\x4b\xa1\x4f\x9f\xe6\x3f\x0d\xab\xb6\x8c\xdb\x4f\x9f\x7e\x33\xc0\x58\x18\x1c\x8c\x27\xa6\xf1\x00\x46\xb7\x02\xf7\x53\xee\x27\x9b\xc2\x5c\x7d\xd8\xed\xd0\x47\xdd\xbe\x7b\x4a\x13\xbf\xc0\x42\xab\xca\x35\x2e\x9c\x18\xe6\xfd\xbd\x3a\xac\xb0\xda\xbc\x5c\x63\x72\xe7\xc6\xbd\x67\x17\xf6\x05\x87\x1e\x94\xd8\x97\x62\x7f\xc1\x25\xb0\x21\xa3\x48\x5e\xe5\x70\x18\xe7\x4d\x53\x3a\x69\xc5\xbe\x29\xe3\xa2\xcb\x23\xc6\x60\xac\x66\xdf\x07\x73\x91\xe5\x2b\x99\xd1\x56\x06\x86\xc5\x96\x91\x71\xaf\x06\xf8\xb1\x12\x60\x81\xc3\xa5\x69\xaa\x84\xcb\x47\x56\x82\x76\xcc\x3c\xee\x2c\x2a\xc1\xbe\xf1\xa8\xf4\x3b\x37\x74\xdd\xc7\xfe\xdb\x65\xe8\xda\x5c\x17\x6c\x4d\x7c\x3a\x77\x04\xf6\x89\xa3\x85\x23\x6c\x0f\xe6\xca\xaa\xe3\xfb\x09\xab\x31\xdf\xb9\xe1\x2f\xdc\xe8\xc6\xaa\xfd\x7a\x4a\x0b\x0b\xda\x4b\xbc\x5a\xb3\x39\x0f\xdd\x88\x82\x85\xb9\xbf\xf2\x4b\xa8\xf1\x07\xfc\x20\x6f\xa3\x27\x45\xcc\x21\xd1\x90\xdc\x20\x45\x32\xf7\x73\x95\x1d\x54\xb2\xa2\x5c\x80\xb4\xb5\x92\xd7\x9c\x83\x37\xa4\x10\x68\xb5\xa6\x08\x89\x57\x78\x0d\xd7\x28\x61\x85\xc9\xdd\x91\x12\x57\x9c\x22\x17\x09\x73\x2c\xdb\x90\x6a\xd3\x37\xe7\x14\xdc\xc8\x4e\xef\x49\x7c\xc5\x44\x17\x4b\x72\xb8\xd6\xab\x27\x1d\xcc\xce\xa4\x13\xc4\xaa\x43\x49\x6e\xa1\x94\x38\x25\xae\x9c\x12\x17\x4e\x09\xf9\x2e\x8e\x21\xc3\x57\x8c\xac\x6b\x0e\x5f\x79\x0b\x77\xc1\x9d\xd9\x65\xc9\xb8\x42\xfd\x15\x4b\xa0\x7a\xac\x97\xbf\xf0\xa2\xc9\x6a\xeb\x0c\xe0\x7a\xeb\x65\xea\x4f\x92\xed\xbe\x6a\xbb\x0f\x7e\x60\xad\xdd\xf9\x2a\xd7\x35\xc2\x0f\x23\xdf\x2d\xc7\xda\x12\x4d\x43\x06\x38\x72\x77\x85\xa3\xf3\x41\x7a\xee\xbb\xfc\x5a\xa5\x15\xd7\xf9\xf5\x2f\xa2\xa1\x55\x57\x4d\x65\x09\xde\xa5\x07\xc8\x41\xe8\x5d\xf5\xf1\x1e\x18\xa2\x55\xf7\xaa\xfe\x67\xd8\x65\x35\x99\xe0\x4d\x53\x84\x17\x14\x03\x8d\xd0\xe5\x4d\xb3\x9e\x15\xa5\x38\x4e\xf3\x59\xc5\x04\x57\x72\x32\x64\x02\x09\x9d\x7b\x37\x0d\xca\x8f\x5d\xe7\xd7\xcc\x92\x20\x78\x97\x61\xf9\x3d\x9a\xb3\xdf\x47\x37\xd6\x98\xfb\x2e\x6c\xa4\xb0\xad\x48\x2d\x76\xc6\xea\x57\x5d\x8b\xb4\x6e\x19\x0c\x2b\x27\x6e\x2b\x45\x33\xd3\xac\xfa\x6c\xa8\x0a\x8c\x36\xaf\x98\x71\x5d\x1e\xbc\x62\xc5\x68\xcc\xa1\x2b\x5a\x0e\x24\x6e\x7c\x3c\xc8\x4c\x53\xa5\x35\xe4\x5d\x30\xf2\x0e\x98\x3b\xd9\xf8\x0a\xbf\x39\x73\x79\xc5\x2a\xce\x21\x53\x56\xf2\x77\xf8\xda\x5b\xc9\xbe\x48\xfc\x9f\x9b\x35\x55\x15\xb7\xff\x2b\x33\x0d\xe3\xfd\xf6\x60\x35\xa7\x3c\x76\xa6\x5d\x7c\x5b\x11\xff\x62\xe5\x88\x60\x2b\x28\x45\xc4\x3c\x92\x14\x6c\x18\xdd\x1d\x99\x01\x6e\x28\x55\x14\x49\x6a\x9d\xbc\xfd\x0c\x8d\xb3\xd9\x99\xe1\x2b\x57\x9c\xe8\xdb\xfb\x07\x2d\x92\xea\x0b\xd3\xf1\xd3\xee\x13\xd3\xe8\x35\x4b\x59\x06\x39\x07\xb7\x11\xe0\xb9\x20\xb9\xff\x5b\x88\x64\x73\x42\x7c\x12\x25\xaa\xcf\xef\x86\xd0\x62\x55\x17\xd1\xf5\x8b\xb6\xcc\x5e\xd4\x59\xdc\x66\x7b\xd4\xf3\x3f\xbf\x0b\xd0\xf7\x0f\x57\xb3\xb4\x16\xe7\x09\x4d\xcf\x7f\xbf\x38\x7f\x24\x13\xae\x53\xdb\x1b\x51\xbb\xdd\xd0\xbf\xab\x3a\x25\x75\x3e\xdb\xd4\x4b\x54\x9b\x58\xd6\x6d\x7a\x6a\x8a\xd0\x6d\x1a\x81\x88\x59\x94\xf9\x99\xed\xdd\xa9\xaf\xd8\x54\x56\x68\x21\xf3\x40\x6e\x8a\x50\x72\xf5\x9d\x8a\x65\x18\x81\x0c\x8b\xd6\x03\xcd\x50\xa8\x6c\xa3\x65\x18\x50\xe1\x8d\xdd\x7f\xcb\x51\xd9\x76\x90\x51\xf4\x67\x65\x3c\xc8\x2d\xcc\x6e\xdb\x42\x90\x3b\x5f\x25\xe6\x77\xbf\x4a\x94\x3c\xe8\xdd\xc0\x7c\xf3\xbd\x9f\xe5\x35\x8d\xc7\x37\x88\xca\xfb\xb9\x41\xe1\xc4\x90\x53\x54\xa4\xbe\x29\x2a\x49\xa7\x3b\xa5\xaa\x9f\xa1\x18\x2f\x73\xc4\x56\x96\xea\x61\xa6\xc3\x34\x87\xca\x89\x29\x30\x37\xcd\x61\xae\x8a\xba\x9a\xa6\xbf\x0d\xab\xa2\x22\x72\x7d\xbb\xf4\x6b\xe5\xb8\x0c\xb1\x87\x51\x6b\x00\x6e\x58\x43\x81\x09\x62\x0a\x43\xd9\x34\xc3\x9c\xf7\x5e\xb1\xeb\x0f\xe5\xdf\x95\x2e\x6b\xb9\x73\xc5\x96\x84\x69\xd7\xae\x8b\x8b\x58\xd2\xa7\x5c\xf8\x0b\x96\xf6\x74\xe2\x51\xe2\x93\x33\xef\x06\x65\x58\x07\xb5\xce\x22\xcb\x49\x3d\x1d\x62\x3e\xa9\xfb\x60\x9e\x5a\x42\x6a\xe8\xa0\xf6\x9f\x49\x63\x1a\xb9\xfe\x66\xb9\x0d\x15\xf3\xbb\xb7\xb7\x4c\xe8\x8f\x7f\x42\x72\xa6\xab\x10\xb7\xaa\x7d\x6a\x62\x8c\xf6\xa3\xbf\x89\x2e\x8e\x1c\xa8\x52\xb8\xa9\x81\x78\xae\xde\x37\xe5\xe7\x3d\x8b\xea\xef\x91\xc4\xd6\xb9\x95\x0f\xbe\xff\x21\xf7\x46\x45\x5b\xb5\x2a\x94\xef\xbf\x77\xa2\xbd\xb6\xdf\x80\x6e\x38\x46\xda\x76\x90\x4f\xe4\x74\x17\xb3\xb6\x1e\x6c\x52\xa0\x3b\xb5\xf0\xbc\x4f\x03\x88\x2e\x30\x26\x42\xf1\xa0\x78\xd1\x4f\x2e\x2c\x8b\xe7\x93\x62\x1a\x56\xea\x6b\x5d\xad\x53\xf2\x49\x61\x79\x24\xce\xfa\x01\x5d\x0e\xfa\xc9\xa2\xae\xe9\xa8\x6a\x5c\x6a\x98\xee\x60\xd5\xeb\xcf\xed\xbb\x80\x7e\x67\xc9\xb6\x7e\x64\x9b\xaa\xa2\x48\x6c\x22\x75\xcb\x70\x0c\x4b\x6c\x5c\x62\xc1\x2d\xe6\x86\x59\x64\x90\xdf\x24\x2c\x83\x5b\xd9\x06\x60\x7a\x87\xc5\x75\xd9\x5a\xd6\xb9\xc5\x86\xeb\x18\x81\x65\x65\xe4\x04\xab\x6f\xd0\x04\x16\x96\xe8\x0b\x0c\xab\x8d\xc8\x5a\x56\x16\x56\x9b\x69\x06\x64\x36\x56\x81\x6d\x6f\x4d\xb5\xb0\xd0\x33\x2b\x65\x33\x36\x75\x65\xfa\x93\xf7\x2d\x9c\x33\xbe\x89\xd1\x36\x98\xc6\x1b\xe6\x18\x08\xbc\x63\x48\x81\x2c\xf4\x9c\x09\xee\xaf\x88\x0f\x68\x33\x33\x1d\xf7\xeb\x6a\x87\x4f\x73\x8b\x7d\x72\x3e\xcd\x77\x79\xd4\xd0\xaf\xc5\x99\x98\x58\xf6\x34\xa2\xc7\xe8\xc9\x88\xdc\x26\x65\x70\x63\x21\x53\x58\xe9\x67\x75\xd5\x0a\xd7\xd8\x56\xeb\x0e\x2e\xf3\x3c\x15\xb3\x6c\x90\x17\x83\x4b\x99\xcd\x8a\x9b\xc1\x9c\xc2\x4d\x03\xae\x50\x7f\x49\x25\xb3\xc5\x60\x95\xcf\x85\x01\x97\xdd\x87\xe9\x03\x62\xd4\xc1\x72\x56\x0e\x56\x79\x21\x06\xd5\x72\x96\x0d\xbc\xa7\x83\x52\x2e\x32\x99\xc8\x78\x96\x55\x1a\x48\x69\xc0\x39\x1a\xae\x37\xde\xdb\x7f\xfa\xec\xe0\xf9\xe1\xec\x32\x9e\x8b\x64\xb1\x94\x5f\xbe\xa6\xab\x2c\x5f\x7f\x2b\xca\xaa\xbe\xba\xfe\x7e\xf3\xe3\xe5\x6f\xaf\x5e\x1f\x1d\xbf\xf9\xd7\xc9\xef\xff\xdf\xe9\xdb\xb3\xf3\x77\xff\xff\xfb\x8b\x0f\x1f\xff\xf8\xf3\xaf\xff\xfa\xef\x27\x9f\x0d\x38\x43\x4f\x78\xfb\x70\x83\xde\x3e\x5c\xdc\x2f\xec\xf5\xe0\x3d\x4e\x3c\x32\x3f\x9e\xeb\x82\x27\xf6\xc0\x13\xfb\xe0\x89\xa7\xe0\x89\x67\xe0\x89\x03\xf0\xc4\x73\xf0\xc4\x21\x78\x82\x06\x09\xcf\xa3\x3f\x63\xfa\xb3\x37\x85\x97\xea\x43\x8e\x23\xf4\xc4\xa1\xfa\xa2\x4a\x55\x51\x1a\xdd\xf1\x6c\x8a\x9d\xe7\x22\x91\x99\x30\x4d\xfd\xeb\xcc\x56\x73\xae\x1f\xd9\x43\x53\x33\xbb\xdd\x7c\xb7\x69\xd4\x99\x1e\x37\xdf\x54\x7f\xab\x0b\x1b\x61\x9a\xfa\xd7\x21\x2f\xab\xa8\xf4\x05\xc0\xdd\x26\x9c\xc1\x70\xc9\xab\xe2\xe6\xe7\x12\x0b\xf1\xad\x96\x85\x60\x6d\x3d\xa8\xc1\x6f\xe3\x59\x15\x2f\xd9\x6b\xfe\xf3\x56\x73\xa0\x70\xfa\x2f\xcb\x70\x76\xdb\x66\x05\xfe\x63\x34\xfa\xcf\x41\x99\xd7\x45\x2c\xde\xce\xd6\x6b\x99\x2d\x3e\xbe\x3f\xc5\x79\x1e\xdf\xf9\xf7\x1a\xce\x6a\xb6\xfe\x8f\xff\x17\x00\x00\xff\xff\x2f\x88\x72\xca\xa2\x43\x00\x00") + +func bignumberJsBytes() ([]byte, error) { + return bindataRead( + _bignumberJs, + "bignumber.js", + ) +} + +func bignumberJs() (*asset, error) { + bytes, err := bignumberJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "bignumber.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "bignumber.js": bignumberJs, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "bignumber.js": {bignumberJs, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/signer/rules/deps/deps.go b/signer/rules/deps/deps.go new file mode 100644 index 0000000000..5ee00b900a --- /dev/null +++ b/signer/rules/deps/deps.go @@ -0,0 +1,21 @@ +// Copyright 2018 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 . + +// Package deps contains the console JavaScript dependencies Go embedded. +package deps + +//go:generate go-bindata -nometadata -pkg deps -o bindata.go bignumber.js +//go:generate gofmt -w -s bindata.go diff --git a/signer/rules/rules.go b/signer/rules/rules.go new file mode 100644 index 0000000000..fa270436df --- /dev/null +++ b/signer/rules/rules.go @@ -0,0 +1,248 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package rules + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/signer/core" + "github.com/ethereum/go-ethereum/signer/rules/deps" + "github.com/ethereum/go-ethereum/signer/storage" + "github.com/robertkrimen/otto" +) + +var ( + BigNumber_JS = deps.MustAsset("bignumber.js") +) + +// consoleOutput is an override for the console.log and console.error methods to +// stream the output into the configured output stream instead of stdout. +func consoleOutput(call otto.FunctionCall) otto.Value { + output := []string{"JS:> "} + for _, argument := range call.ArgumentList { + output = append(output, fmt.Sprintf("%v", argument)) + } + fmt.Fprintln(os.Stdout, strings.Join(output, " ")) + return otto.Value{} +} + +// rulesetUi provides an implementation of SignerUI that evaluates a javascript +// file for each defined UI-method +type rulesetUi struct { + next core.SignerUI // The next handler, for manual processing + storage storage.Storage + credentials storage.Storage + jsRules string // The rules to use +} + +func NewRuleEvaluator(next core.SignerUI, jsbackend, credentialsBackend storage.Storage) (*rulesetUi, error) { + c := &rulesetUi{ + next: next, + storage: jsbackend, + credentials: credentialsBackend, + jsRules: "", + } + + return c, nil +} + +func (r *rulesetUi) Init(javascriptRules string) error { + r.jsRules = javascriptRules + return nil +} +func (r *rulesetUi) execute(jsfunc string, jsarg interface{}) (otto.Value, error) { + + // Instantiate a fresh vm engine every time + vm := otto.New() + // Set the native callbacks + consoleObj, _ := vm.Get("console") + consoleObj.Object().Set("log", consoleOutput) + consoleObj.Object().Set("error", consoleOutput) + vm.Set("storage", r.storage) + + // Load bootstrap libraries + script, err := vm.Compile("bignumber.js", BigNumber_JS) + if err != nil { + log.Warn("Failed loading libraries", "err", err) + return otto.UndefinedValue(), err + } + vm.Run(script) + + // Run the actual rule implementation + _, err = vm.Run(r.jsRules) + if err != nil { + log.Warn("Execution failed", "err", err) + return otto.UndefinedValue(), err + } + + // And the actual call + // All calls are objects with the parameters being keys in that object. + // To provide additional insulation between js and go, we serialize it into JSON on the Go-side, + // and deserialize it on the JS side. + + jsonbytes, err := json.Marshal(jsarg) + if err != nil { + log.Warn("failed marshalling data", "data", jsarg) + return otto.UndefinedValue(), err + } + // Now, we call foobar(JSON.parse()). + var call string + if len(jsonbytes) > 0 { + call = fmt.Sprintf("%v(JSON.parse(%v))", jsfunc, string(jsonbytes)) + } else { + call = fmt.Sprintf("%v()", jsfunc) + } + return vm.Run(call) +} + +func (r *rulesetUi) checkApproval(jsfunc string, jsarg []byte, err error) (bool, error) { + if err != nil { + return false, err + } + v, err := r.execute(jsfunc, string(jsarg)) + if err != nil { + log.Info("error occurred during execution", "error", err) + return false, err + } + result, err := v.ToString() + if err != nil { + log.Info("error occurred during response unmarshalling", "error", err) + return false, err + } + if result == "Approve" { + log.Info("Op approved") + return true, nil + } else if result == "Reject" { + log.Info("Op rejected") + return false, nil + } + return false, fmt.Errorf("Unknown response") +} + +func (r *rulesetUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { + jsonreq, err := json.Marshal(request) + approved, err := r.checkApproval("ApproveTx", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", err) + return r.next.ApproveTx(request) + } + + if approved { + return core.SignTxResponse{ + Transaction: request.Transaction, + Approved: true, + Password: r.lookupPassword(request.Transaction.From.Address()), + }, + nil + } + return core.SignTxResponse{Approved: false}, err +} + +func (r *rulesetUi) lookupPassword(address common.Address) string { + return r.credentials.Get(strings.ToLower(address.String())) +} + +func (r *rulesetUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { + jsonreq, err := json.Marshal(request) + approved, err := r.checkApproval("ApproveSignData", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", err) + return r.next.ApproveSignData(request) + } + if approved { + return core.SignDataResponse{Approved: true, Password: r.lookupPassword(request.Address.Address())}, nil + } + return core.SignDataResponse{Approved: false, Password: ""}, err +} + +func (r *rulesetUi) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { + jsonreq, err := json.Marshal(request) + approved, err := r.checkApproval("ApproveExport", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", err) + return r.next.ApproveExport(request) + } + if approved { + return core.ExportResponse{Approved: true}, nil + } + return core.ExportResponse{Approved: false}, err +} + +func (r *rulesetUi) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { + // This cannot be handled by rules, requires setting a password + // dispatch to next + return r.next.ApproveImport(request) +} + +func (r *rulesetUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { + jsonreq, err := json.Marshal(request) + approved, err := r.checkApproval("ApproveListing", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", err) + return r.next.ApproveListing(request) + } + if approved { + return core.ListResponse{Accounts: request.Accounts}, nil + } + return core.ListResponse{}, err +} + +func (r *rulesetUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { + // This cannot be handled by rules, requires setting a password + // dispatch to next + return r.next.ApproveNewAccount(request) +} + +func (r *rulesetUi) ShowError(message string) { + log.Error(message) + r.next.ShowError(message) +} + +func (r *rulesetUi) ShowInfo(message string) { + log.Info(message) + r.next.ShowInfo(message) +} +func (r *rulesetUi) OnSignerStartup(info core.StartupInfo) { + jsonInfo, err := json.Marshal(info) + if err != nil { + log.Warn("failed marshalling data", "data", info) + return + } + r.next.OnSignerStartup(info) + _, err = r.execute("OnSignerStartup", string(jsonInfo)) + if err != nil { + log.Info("error occurred during execution", "error", err) + } +} + +func (r *rulesetUi) OnApprovedTx(tx ethapi.SignTransactionResult) { + jsonTx, err := json.Marshal(tx) + if err != nil { + log.Warn("failed marshalling transaction", "tx", tx) + return + } + _, err = r.execute("OnApprovedTx", string(jsonTx)) + if err != nil { + log.Info("error occurred during execution", "error", err) + } +} diff --git a/signer/rules/rules_test.go b/signer/rules/rules_test.go new file mode 100644 index 0000000000..6a04035004 --- /dev/null +++ b/signer/rules/rules_test.go @@ -0,0 +1,631 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . +// +package rules + +import ( + "fmt" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/signer/core" + "github.com/ethereum/go-ethereum/signer/storage" +) + +const JS = ` +/** +This is an example implementation of a Javascript rule file. + +When the signer receives a request over the external API, the corresponding method is evaluated. +Three things can happen: + +1. The method returns "Approve". This means the operation is permitted. +2. The method returns "Reject". This means the operation is rejected. +3. Anything else; other return values [*], method not implemented or exception occurred during processing. This means +that the operation will continue to manual processing, via the regular UI method chosen by the user. + +[*] Note: Future version of the ruleset may use more complex json-based returnvalues, making it possible to not +only respond Approve/Reject/Manual, but also modify responses. For example, choose to list only one, but not all +accounts in a list-request. The points above will continue to hold for non-json based responses ("Approve"/"Reject"). + +**/ + +function ApproveListing(request){ + console.log("In js approve listing"); + console.log(request.accounts[3].Address) + console.log(request.meta.Remote) + return "Approve" +} + +function ApproveTx(request){ + console.log("test"); + console.log("from"); + return "Reject"; +} + +function test(thing){ + console.log(thing.String()) +} + +` + +func mixAddr(a string) (*common.MixedcaseAddress, error) { + return common.NewMixedcaseAddressFromString(a) +} + +type alwaysDenyUi struct{} + +func (alwaysDenyUi) OnSignerStartup(info core.StartupInfo) { +} + +func (alwaysDenyUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { + return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil +} + +func (alwaysDenyUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { + return core.SignDataResponse{Approved: false, Password: ""}, nil +} + +func (alwaysDenyUi) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { + return core.ExportResponse{Approved: false}, nil +} + +func (alwaysDenyUi) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { + return core.ImportResponse{Approved: false, OldPassword: "", NewPassword: ""}, nil +} + +func (alwaysDenyUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { + return core.ListResponse{Accounts: nil}, nil +} + +func (alwaysDenyUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { + return core.NewAccountResponse{Approved: false, Password: ""}, nil +} + +func (alwaysDenyUi) ShowError(message string) { + panic("implement me") +} + +func (alwaysDenyUi) ShowInfo(message string) { + panic("implement me") +} + +func (alwaysDenyUi) OnApprovedTx(tx ethapi.SignTransactionResult) { + panic("implement me") +} + +func initRuleEngine(js string) (*rulesetUi, error) { + r, err := NewRuleEvaluator(&alwaysDenyUi{}, storage.NewEphemeralStorage(), storage.NewEphemeralStorage()) + if err != nil { + return nil, fmt.Errorf("failed to create js engine: %v", err) + } + if err = r.Init(js); err != nil { + return nil, fmt.Errorf("failed to load bootstrap js: %v", err) + } + return r, nil +} + +func TestListRequest(t *testing.T) { + accs := make([]core.Account, 5) + + for i := range accs { + addr := fmt.Sprintf("000000000000000000000000000000000000000%x", i) + acc := core.Account{ + Address: common.BytesToAddress(common.Hex2Bytes(addr)), + URL: accounts.URL{Scheme: "test", Path: fmt.Sprintf("acc-%d", i)}, + } + accs[i] = acc + } + + js := `function ApproveListing(){ return "Approve" }` + + r, err := initRuleEngine(js) + if err != nil { + t.Errorf("Couldn't create evaluator %v", err) + return + } + resp, err := r.ApproveListing(&core.ListRequest{ + Accounts: accs, + Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, + }) + if len(resp.Accounts) != len(accs) { + t.Errorf("Expected check to resolve to 'Approve'") + } +} + +func TestSignTxRequest(t *testing.T) { + + js := ` + function ApproveTx(r){ + console.log("transaction.from", r.transaction.from); + console.log("transaction.to", r.transaction.to); + console.log("transaction.value", r.transaction.value); + console.log("transaction.nonce", r.transaction.nonce); + if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"} + if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"} + }` + + r, err := initRuleEngine(js) + if err != nil { + t.Errorf("Couldn't create evaluator %v", err) + return + } + to, err := mixAddr("000000000000000000000000000000000000dead") + if err != nil { + t.Error(err) + return + } + from, err := mixAddr("0000000000000000000000000000000000001337") + + if err != nil { + t.Error(err) + return + } + fmt.Printf("to %v", to.Address().String()) + resp, err := r.ApproveTx(&core.SignTxRequest{ + Transaction: core.SendTxArgs{ + From: *from, + To: to}, + Callinfo: nil, + Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, + }) + if err != nil { + t.Errorf("Unexpected error %v", err) + } + if !resp.Approved { + t.Errorf("Expected check to resolve to 'Approve'") + } +} + +type dummyUi struct { + calls []string +} + +func (d *dummyUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { + d.calls = append(d.calls, "ApproveTx") + return core.SignTxResponse{}, core.ErrRequestDenied +} + +func (d *dummyUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { + d.calls = append(d.calls, "ApproveSignData") + return core.SignDataResponse{}, core.ErrRequestDenied +} + +func (d *dummyUi) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { + d.calls = append(d.calls, "ApproveExport") + return core.ExportResponse{}, core.ErrRequestDenied +} + +func (d *dummyUi) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { + d.calls = append(d.calls, "ApproveImport") + return core.ImportResponse{}, core.ErrRequestDenied +} + +func (d *dummyUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { + d.calls = append(d.calls, "ApproveListing") + return core.ListResponse{}, core.ErrRequestDenied +} + +func (d *dummyUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { + d.calls = append(d.calls, "ApproveNewAccount") + return core.NewAccountResponse{}, core.ErrRequestDenied +} + +func (d *dummyUi) ShowError(message string) { + d.calls = append(d.calls, "ShowError") +} + +func (d *dummyUi) ShowInfo(message string) { + d.calls = append(d.calls, "ShowInfo") +} + +func (d *dummyUi) OnApprovedTx(tx ethapi.SignTransactionResult) { + d.calls = append(d.calls, "OnApprovedTx") +} +func (d *dummyUi) OnSignerStartup(info core.StartupInfo) { +} + +//TestForwarding tests that the rule-engine correctly dispatches requests to the next caller +func TestForwarding(t *testing.T) { + + js := "" + ui := &dummyUi{make([]string, 0)} + jsBackend := storage.NewEphemeralStorage() + credBackend := storage.NewEphemeralStorage() + r, err := NewRuleEvaluator(ui, jsBackend, credBackend) + if err != nil { + t.Fatalf("Failed to create js engine: %v", err) + } + if err = r.Init(js); err != nil { + t.Fatalf("Failed to load bootstrap js: %v", err) + } + r.ApproveSignData(nil) + r.ApproveTx(nil) + r.ApproveImport(nil) + r.ApproveNewAccount(nil) + r.ApproveListing(nil) + r.ApproveExport(nil) + r.ShowError("test") + r.ShowInfo("test") + + //This one is not forwarded + r.OnApprovedTx(ethapi.SignTransactionResult{}) + + expCalls := 8 + if len(ui.calls) != expCalls { + + t.Errorf("Expected %d forwarded calls, got %d: %s", expCalls, len(ui.calls), strings.Join(ui.calls, ",")) + + } + +} + +func TestMissingFunc(t *testing.T) { + r, err := initRuleEngine(JS) + if err != nil { + t.Errorf("Couldn't create evaluator %v", err) + return + } + + _, err = r.execute("MissingMethod", "test") + + if err == nil { + t.Error("Expected error") + } + + approved, err := r.checkApproval("MissingMethod", nil, nil) + if err == nil { + t.Errorf("Expected missing method to yield error'") + } + if approved { + t.Errorf("Expected missing method to cause non-approval") + } + fmt.Printf("Err %v", err) + +} +func TestStorage(t *testing.T) { + + js := ` + function testStorage(){ + storage.Put("mykey", "myvalue") + a = storage.Get("mykey") + + storage.Put("mykey", ["a", "list"]) // Should result in "a,list" + a += storage.Get("mykey") + + + storage.Put("mykey", {"an": "object"}) // Should result in "[object Object]" + a += storage.Get("mykey") + + + storage.Put("mykey", JSON.stringify({"an": "object"})) // Should result in '{"an":"object"}' + a += storage.Get("mykey") + + a += storage.Get("missingkey") //Missing keys should result in empty string + storage.Put("","missing key==noop") // Can't store with 0-length key + a += storage.Get("") // Should result in '' + + var b = new BigNumber(2) + var c = new BigNumber(16)//"0xf0",16) + var d = b.plus(c) + console.log(d) + return a + } +` + r, err := initRuleEngine(js) + if err != nil { + t.Errorf("Couldn't create evaluator %v", err) + return + } + + v, err := r.execute("testStorage", nil) + + if err != nil { + t.Errorf("Unexpected error %v", err) + } + + retval, err := v.ToString() + + if err != nil { + t.Errorf("Unexpected error %v", err) + } + exp := `myvaluea,list[object Object]{"an":"object"}` + if retval != exp { + t.Errorf("Unexpected data, expected '%v', got '%v'", exp, retval) + } + fmt.Printf("Err %v", err) + +} + +const ExampleTxWindow = ` + function big(str){ + if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)} + return new BigNumber(str) + } + + // Time window: 1 week + var window = 1000* 3600*24*7; + + // Limit : 1 ether + var limit = new BigNumber("1e18"); + + function isLimitOk(transaction){ + var value = big(transaction.value) + // Start of our window function + var windowstart = new Date().getTime() - window; + + var txs = []; + var stored = storage.Get('txs'); + + if(stored != ""){ + txs = JSON.parse(stored) + } + // First, remove all that have passed out of the time-window + var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart}); + console.log(txs, newtxs.length); + + // Secondly, aggregate the current sum + sum = new BigNumber(0) + + sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum); + console.log("ApproveTx > Sum so far", sum); + console.log("ApproveTx > Requested", value.toNumber()); + + // Would we exceed weekly limit ? + return sum.plus(value).lt(limit) + + } + function ApproveTx(r){ + console.log(r) + console.log(typeof(r)) + if (isLimitOk(r.transaction)){ + return "Approve" + } + return "Nope" + } + + /** + * OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter + * 'response_str' contains the return value that will be sent to the external caller. + * The return value from this method is ignore - the reason for having this callback is to allow the + * ruleset to keep track of approved transactions. + * + * When implementing rate-limited rules, this callback should be used. + * If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user + * then accepts the transaction, this method will be called. + * + * TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx. + */ + function OnApprovedTx(resp){ + var value = big(resp.tx.value) + var txs = [] + // Load stored transactions + var stored = storage.Get('txs'); + if(stored != ""){ + txs = JSON.parse(stored) + } + // Add this to the storage + txs.push({tstamp: new Date().getTime(), value: value}); + storage.Put("txs", JSON.stringify(txs)); + } + +` + +func dummyTx(value hexutil.Big) *core.SignTxRequest { + + to, _ := mixAddr("000000000000000000000000000000000000dead") + from, _ := mixAddr("000000000000000000000000000000000000dead") + n := hexutil.Uint64(3) + gas := hexutil.Uint64(21000) + gasPrice := hexutil.Big(*big.NewInt(2000000)) + + return &core.SignTxRequest{ + Transaction: core.SendTxArgs{ + From: *from, + To: to, + Value: value, + Nonce: n, + GasPrice: gasPrice, + Gas: gas, + }, + Callinfo: []core.ValidationInfo{ + {Typ: "Warning", Message: "All your base are bellong to us"}, + }, + Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, + } +} +func dummyTxWithV(value uint64) *core.SignTxRequest { + + v := big.NewInt(0).SetUint64(value) + h := hexutil.Big(*v) + return dummyTx(h) +} +func dummySigned(value *big.Int) *types.Transaction { + to := common.HexToAddress("000000000000000000000000000000000000dead") + gas := uint64(21000) + gasPrice := big.NewInt(2000000) + data := make([]byte, 0) + return types.NewTransaction(3, to, value, gas, gasPrice, data) + +} +func TestLimitWindow(t *testing.T) { + + r, err := initRuleEngine(ExampleTxWindow) + if err != nil { + t.Errorf("Couldn't create evaluator %v", err) + return + } + + // 0.3 ether: 429D069189E0000 wei + v := big.NewInt(0).SetBytes(common.Hex2Bytes("0429D069189E0000")) + h := hexutil.Big(*v) + // The first three should succeed + for i := 0; i < 3; i++ { + unsigned := dummyTx(h) + resp, err := r.ApproveTx(unsigned) + if err != nil { + t.Errorf("Unexpected error %v", err) + } + if !resp.Approved { + t.Errorf("Expected check to resolve to 'Approve'") + } + // Create a dummy signed transaction + + response := ethapi.SignTransactionResult{ + Tx: dummySigned(v), + Raw: common.Hex2Bytes("deadbeef"), + } + r.OnApprovedTx(response) + } + // Fourth should fail + resp, err := r.ApproveTx(dummyTx(h)) + if resp.Approved { + t.Errorf("Expected check to resolve to 'Reject'") + } + +} + +// dontCallMe is used as a next-handler that does not want to be called - it invokes test failure +type dontCallMe struct { + t *testing.T +} + +func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) { +} + +func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { + d.t.Fatalf("Did not expect next-handler to be called") + return core.SignTxResponse{}, core.ErrRequestDenied +} + +func (d *dontCallMe) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { + d.t.Fatalf("Did not expect next-handler to be called") + return core.SignDataResponse{}, core.ErrRequestDenied +} + +func (d *dontCallMe) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { + d.t.Fatalf("Did not expect next-handler to be called") + return core.ExportResponse{}, core.ErrRequestDenied +} + +func (d *dontCallMe) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { + d.t.Fatalf("Did not expect next-handler to be called") + return core.ImportResponse{}, core.ErrRequestDenied +} + +func (d *dontCallMe) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { + d.t.Fatalf("Did not expect next-handler to be called") + return core.ListResponse{}, core.ErrRequestDenied +} + +func (d *dontCallMe) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { + d.t.Fatalf("Did not expect next-handler to be called") + return core.NewAccountResponse{}, core.ErrRequestDenied +} + +func (d *dontCallMe) ShowError(message string) { + d.t.Fatalf("Did not expect next-handler to be called") +} + +func (d *dontCallMe) ShowInfo(message string) { + d.t.Fatalf("Did not expect next-handler to be called") +} + +func (d *dontCallMe) OnApprovedTx(tx ethapi.SignTransactionResult) { + d.t.Fatalf("Did not expect next-handler to be called") +} + +//TestContextIsCleared tests that the rule-engine does not retain variables over several requests. +// if it does, that would be bad since developers may rely on that to store data, +// instead of using the disk-based data storage +func TestContextIsCleared(t *testing.T) { + + js := ` + function ApproveTx(){ + if (typeof foobar == 'undefined') { + foobar = "Approve" + } + console.log(foobar) + if (foobar == "Approve"){ + foobar = "Reject" + }else{ + foobar = "Approve" + } + return foobar + } + ` + ui := &dontCallMe{t} + r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage(), storage.NewEphemeralStorage()) + if err != nil { + t.Fatalf("Failed to create js engine: %v", err) + } + if err = r.Init(js); err != nil { + t.Fatalf("Failed to load bootstrap js: %v", err) + } + tx := dummyTxWithV(0) + r1, err := r.ApproveTx(tx) + r2, err := r.ApproveTx(tx) + if r1.Approved != r2.Approved { + t.Errorf("Expected execution context to be cleared between executions") + } +} + +func TestSignData(t *testing.T) { + + js := `function ApproveListing(){ + return "Approve" +} +function ApproveSignData(r){ + if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa") + { + if(r.message.indexOf("bazonk") >= 0){ + return "Approve" + } + return "Reject" + } + // Otherwise goes to manual processing +}` + r, err := initRuleEngine(js) + if err != nil { + t.Errorf("Couldn't create evaluator %v", err) + return + } + message := []byte("baz bazonk foo") + hash, msg := core.SignHash(message) + raw := hexutil.Bytes(message) + addr, _ := mixAddr("0x694267f14675d7e1b9494fd8d72fefe1755710fa") + + fmt.Printf("address %v %v\n", addr.String(), addr.Original()) + resp, err := r.ApproveSignData(&core.SignDataRequest{ + Address: *addr, + Message: msg, + Hash: hash, + Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, + Rawdata: raw, + }) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if !resp.Approved { + t.Fatalf("Expected approved") + } +} diff --git a/signer/storage/aes_gcm_storage.go b/signer/storage/aes_gcm_storage.go new file mode 100644 index 0000000000..1ac3475588 --- /dev/null +++ b/signer/storage/aes_gcm_storage.go @@ -0,0 +1,164 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . +// +package storage + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/json" + "io" + "io/ioutil" + "os" + + "github.com/ethereum/go-ethereum/log" +) + +type storedCredential struct { + // The iv + Iv []byte `json:"iv"` + // The ciphertext + CipherText []byte `json:"c"` +} + +// AESEncryptedStorage is a storage type which is backed by a json-faile. The json-file contains +// key-value mappings, where the keys are _not_ encrypted, only the values are. +type AESEncryptedStorage struct { + // File to read/write credentials + filename string + // Key stored in base64 + key []byte +} + +// NewAESEncryptedStorage creates a new encrypted storage backed by the given file/key +func NewAESEncryptedStorage(filename string, key []byte) *AESEncryptedStorage { + return &AESEncryptedStorage{ + filename: filename, + key: key, + } +} + +// Put stores a value by key. 0-length keys results in no-op +func (s *AESEncryptedStorage) Put(key, value string) { + if len(key) == 0 { + return + } + data, err := s.readEncryptedStorage() + if err != nil { + log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename) + return + } + ciphertext, iv, err := encrypt(s.key, []byte(value)) + if err != nil { + log.Warn("Failed to encrypt entry", "err", err) + return + } + encrypted := storedCredential{Iv: iv, CipherText: ciphertext} + data[key] = encrypted + if err = s.writeEncryptedStorage(data); err != nil { + log.Warn("Failed to write entry", "err", err) + } +} + +// Get returns the previously stored value, or the empty string if it does not exist or key is of 0-length +func (s *AESEncryptedStorage) Get(key string) string { + if len(key) == 0 { + return "" + } + data, err := s.readEncryptedStorage() + if err != nil { + log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename) + return "" + } + encrypted, exist := data[key] + if !exist { + log.Warn("Key does not exist", "key", key) + return "" + } + entry, err := decrypt(s.key, encrypted.Iv, encrypted.CipherText) + if err != nil { + log.Warn("Failed to decrypt key", "key", key) + return "" + } + return string(entry) +} + +// readEncryptedStorage reads the file with encrypted creds +func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) { + creds := make(map[string]storedCredential) + raw, err := ioutil.ReadFile(s.filename) + + if err != nil { + if os.IsNotExist(err) { + // Doesn't exist yet + return creds, nil + + } else { + log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename) + } + } + if err = json.Unmarshal(raw, &creds); err != nil { + log.Warn("Failed to unmarshal encrypted storage", "err", err, "file", s.filename) + return nil, err + } + return creds, nil +} + +// writeEncryptedStorage write the file with encrypted creds +func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]storedCredential) error { + raw, err := json.Marshal(creds) + if err != nil { + return err + } + if err = ioutil.WriteFile(s.filename, raw, 0600); err != nil { + return err + } + return nil +} + +func encrypt(key []byte, plaintext []byte) ([]byte, []byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + aesgcm, err := cipher.NewGCM(block) + nonce := make([]byte, aesgcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, nil, err + } + if err != nil { + return nil, nil, err + } + ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil) + return ciphertext, nonce, nil +} + +func decrypt(key []byte, nonce []byte, ciphertext []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + return plaintext, nil +} diff --git a/signer/storage/aes_gcm_storage_test.go b/signer/storage/aes_gcm_storage_test.go new file mode 100644 index 0000000000..77804905ac --- /dev/null +++ b/signer/storage/aes_gcm_storage_test.go @@ -0,0 +1,115 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . +// +package storage + +import ( + "bytes" + "fmt" + "io/ioutil" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/mattn/go-colorable" +) + +func TestEncryption(t *testing.T) { + // key := []byte("AES256Key-32Characters1234567890") + // plaintext := []byte(value) + key := []byte("AES256Key-32Characters1234567890") + plaintext := []byte("exampleplaintext") + + c, iv, err := encrypt(key, plaintext) + if err != nil { + t.Fatal(err) + } + fmt.Printf("Ciphertext %x, nonce %x\n", c, iv) + + p, err := decrypt(key, iv, c) + if err != nil { + t.Fatal(err) + } + fmt.Printf("Plaintext %v\n", string(p)) + if !bytes.Equal(plaintext, p) { + t.Errorf("Failed: expected plaintext recovery, got %v expected %v", string(plaintext), string(p)) + } +} + +func TestFileStorage(t *testing.T) { + + a := map[string]storedCredential{ + "secret": { + Iv: common.Hex2Bytes("cdb30036279601aeee60f16b"), + CipherText: common.Hex2Bytes("f311ac49859d7260c2c464c28ffac122daf6be801d3cfd3edcbde7e00c9ff74f"), + }, + "secret2": { + Iv: common.Hex2Bytes("afb8a7579bf971db9f8ceeed"), + CipherText: common.Hex2Bytes("2df87baf86b5073ef1f03e3cc738de75b511400f5465bb0ddeacf47ae4dc267d"), + }, + } + d, err := ioutil.TempDir("", "eth-encrypted-storage-test") + if err != nil { + t.Fatal(err) + } + stored := &AESEncryptedStorage{ + filename: fmt.Sprintf("%v/vault.json", d), + key: []byte("AES256Key-32Characters1234567890"), + } + stored.writeEncryptedStorage(a) + read := &AESEncryptedStorage{ + filename: fmt.Sprintf("%v/vault.json", d), + key: []byte("AES256Key-32Characters1234567890"), + } + creds, err := read.readEncryptedStorage() + if err != nil { + t.Fatal(err) + } + for k, v := range a { + if v2, exist := creds[k]; !exist { + t.Errorf("Missing entry %v", k) + } else { + if !bytes.Equal(v.CipherText, v2.CipherText) { + t.Errorf("Wrong ciphertext, expected %x got %x", v.CipherText, v2.CipherText) + } + if !bytes.Equal(v.Iv, v2.Iv) { + t.Errorf("Wrong iv") + } + } + } +} +func TestEnd2End(t *testing.T) { + log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(3), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) + + d, err := ioutil.TempDir("", "eth-encrypted-storage-test") + if err != nil { + t.Fatal(err) + } + + s1 := &AESEncryptedStorage{ + filename: fmt.Sprintf("%v/vault.json", d), + key: []byte("AES256Key-32Characters1234567890"), + } + s2 := &AESEncryptedStorage{ + filename: fmt.Sprintf("%v/vault.json", d), + key: []byte("AES256Key-32Characters1234567890"), + } + + s1.Put("bazonk", "foobar") + if v := s2.Get("bazonk"); v != "foobar" { + t.Errorf("Expected bazonk->foobar, got '%v'", v) + } +} diff --git a/signer/storage/storage.go b/signer/storage/storage.go new file mode 100644 index 0000000000..60f4e3892a --- /dev/null +++ b/signer/storage/storage.go @@ -0,0 +1,62 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . +// + +package storage + +import ( + "fmt" +) + +type Storage interface { + // Put stores a value by key. 0-length keys results in no-op + Put(key, value string) + // Get returns the previously stored value, or the empty string if it does not exist or key is of 0-length + Get(key string) string +} + +// EphemeralStorage is an in-memory storage that does +// not persist values to disk. Mainly used for testing +type EphemeralStorage struct { + data map[string]string + namespace string +} + +func (s *EphemeralStorage) Put(key, value string) { + if len(key) == 0 { + return + } + fmt.Printf("storage: put %v -> %v\n", key, value) + s.data[key] = value +} + +func (s *EphemeralStorage) Get(key string) string { + if len(key) == 0 { + return "" + } + fmt.Printf("storage: get %v\n", key) + if v, exist := s.data[key]; exist { + return v + } + return "" +} + +func NewEphemeralStorage() Storage { + s := &EphemeralStorage{ + data: make(map[string]string), + } + return s +} From 92c6d13083a0f672a0379bfd33f67e8dec055590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Tue, 17 Apr 2018 08:33:31 +0200 Subject: [PATCH 35/36] light: new CHTs (#16515) --- light/postprocess.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/light/postprocess.go b/light/postprocess.go index a1b1d9fb04..702eb53c86 100644 --- a/light/postprocess.go +++ b/light/postprocess.go @@ -58,18 +58,18 @@ type trustedCheckpoint struct { var ( mainnetCheckpoint = trustedCheckpoint{ name: "mainnet", - sectionIdx: 161, - sectionHead: common.HexToHash("75b0c4baa7a62cece48abdcb03b6f31601961c9bece67dcd61df87aad4fc0d8d"), - chtRoot: common.HexToHash("bbbfaa67b29716348997ec21a39c03b8d1fb973f6a43740b865595ba26ee812f"), - bloomTrieRoot: common.HexToHash("d6db6e6248354d7453391ce97830072a28ea4216be0bd95a5db9f53b1a64677b"), + sectionIdx: 165, + sectionHead: common.HexToHash("21028acf9cd9ce80257221adc437c3c58ce046c4d43c21c3e9b1d1349059ec73"), + chtRoot: common.HexToHash("26b2458cb7d0080d3a39311c914be92c368777a65ec074e1893b8bdc79e3910a"), + bloomTrieRoot: common.HexToHash("5d06908769179186165a72db7fc3473b25c28ed27efe78a392a9ff2c3fa67f84"), } ropstenCheckpoint = trustedCheckpoint{ name: "ropsten", - sectionIdx: 87, - sectionHead: common.HexToHash("ebc0adcb30ed21cbe95bd77499cc1af0bada621fee3644cb80dbcf1444c123fe"), - chtRoot: common.HexToHash("d9830f4893c821ddf149b8cb9d3e3bfe3109d2eea8e3c4a4ede7c8b2ee8a7800"), - bloomTrieRoot: common.HexToHash("c76e12d713f65b84c5a36d06bc77d0c8419248ea0b36e0812a78b76aa6da0ddb"), + sectionIdx: 92, + sectionHead: common.HexToHash("21a158f9cc643da13a237cafceb37381072649f7278cf98c5820bfbced7cfcec"), + chtRoot: common.HexToHash("1a8ddb8b086d7a33ca90eea90730225948fa504ae0283b15aff3c15c0e089bf9"), + bloomTrieRoot: common.HexToHash("fd192f92afbcdd0020c81ca0625116b5995509659653b10123bd986fe5129cc1"), } ) From 2423ae01e0d2f853512eb2b46954b5ad0754a897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 17 Apr 2018 11:20:53 +0300 Subject: [PATCH 36/36] params: release Geth v1.8.4 --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index 181f84631e..f06a05da25 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 8 // Minor version component of the current release - VersionPatch = 4 // Patch version component of the current release - VersionMeta = "unstable" // Version metadata to append to the version string + VersionMajor = 1 // Major version component of the current release + VersionMinor = 8 // Minor version component of the current release + VersionPatch = 4 // Patch version component of the current release + VersionMeta = "stable" // Version metadata to append to the version string ) // Version holds the textual version string.