diff --git a/ethchain/address.go b/ethchain/address.go deleted file mode 100644 index 0b3ef7c05..000000000 --- a/ethchain/address.go +++ /dev/null @@ -1,76 +0,0 @@ -package ethchain - -import ( - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -type Account struct { - address []byte - Amount *big.Int - Nonce uint64 -} - -func NewAccount(address []byte, amount *big.Int) *Account { - return &Account{address, amount, 0} -} - -func NewAccountFromData(address, data []byte) *Account { - account := &Account{address: address} - account.RlpDecode(data) - - return account -} - -func (a *Account) AddFee(fee *big.Int) { - a.AddFunds(fee) -} - -func (a *Account) AddFunds(funds *big.Int) { - a.Amount.Add(a.Amount, funds) -} - -func (a *Account) Address() []byte { - return a.address -} - -// Implements Callee -func (a *Account) ReturnGas(value *big.Int, state *State) { - // Return the value back to the sender - a.AddFunds(value) - state.UpdateAccount(a.address, a) -} - -func (a *Account) RlpEncode() []byte { - return ethutil.Encode([]interface{}{a.Amount, a.Nonce}) -} - -func (a *Account) RlpDecode(data []byte) { - decoder := ethutil.NewValueFromBytes(data) - - a.Amount = decoder.Get(0).BigInt() - a.Nonce = decoder.Get(1).Uint() -} - -type AddrStateStore struct { - states map[string]*AccountState -} - -func NewAddrStateStore() *AddrStateStore { - return &AddrStateStore{states: make(map[string]*AccountState)} -} - -func (s *AddrStateStore) Add(addr []byte, account *Account) *AccountState { - state := &AccountState{Nonce: account.Nonce, Account: account} - s.states[string(addr)] = state - return state -} - -func (s *AddrStateStore) Get(addr []byte) *AccountState { - return s.states[string(addr)] -} - -type AccountState struct { - Nonce uint64 - Account *Account -} diff --git a/ethchain/address_test.go b/ethchain/address_test.go deleted file mode 100644 index 161e1b251..000000000 --- a/ethchain/address_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package ethchain - -import ( - "testing" -) - -func TestAddressState(t *testing.T) { -} diff --git a/ethchain/block.go b/ethchain/block.go index 732739c1b..8c93947fb 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -142,12 +142,13 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { data := block.state.trie.Get(string(block.Coinbase)) // Get the ether (Coinbase) and add the fee (gief fee to miner) - ether := NewAccountFromData(block.Coinbase, []byte(data)) + account := NewStateObjectFromBytes(block.Coinbase, []byte(data)) base = new(big.Int) - ether.Amount = base.Add(ether.Amount, fee) + account.Amount = base.Add(account.Amount, fee) - block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode())) + //block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode())) + block.state.UpdateStateObject(account) return true } diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 2a50ef687..d65c38fe4 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -262,9 +262,9 @@ func AddTestNetFunds(block *Block) { } { //log.Println("2^200 Wei to", addr) codedAddr := ethutil.FromHex(addr) - addr := block.state.GetAccount(codedAddr) - addr.Amount = ethutil.BigPow(2, 200) - block.state.UpdateAccount(codedAddr, addr) + account := block.state.GetAccount(codedAddr) + account.Amount = ethutil.BigPow(2, 200) + block.state.UpdateStateObject(account) } } diff --git a/ethchain/contract.go b/ethchain/contract.go deleted file mode 100644 index af348667c..000000000 --- a/ethchain/contract.go +++ /dev/null @@ -1,119 +0,0 @@ -package ethchain - -import ( - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -type Contract struct { - Amount *big.Int - Nonce uint64 - //state *ethutil.Trie - state *State - address []byte - script []byte - initScript []byte -} - -func NewContract(address []byte, Amount *big.Int, root []byte) *Contract { - contract := &Contract{address: address, Amount: Amount, Nonce: 0} - contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) - - return contract -} - -func NewContractFromBytes(address, data []byte) *Contract { - contract := &Contract{address: address} - contract.RlpDecode(data) - - return contract -} - -func (c *Contract) Addr(addr []byte) *ethutil.Value { - return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) -} - -func (c *Contract) SetAddr(addr []byte, value interface{}) { - c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) -} - -func (c *Contract) State() *State { - return c.state -} - -func (c *Contract) GetMem(num *big.Int) *ethutil.Value { - nb := ethutil.BigToBytes(num, 256) - - return c.Addr(nb) -} - -func (c *Contract) GetInstr(pc *big.Int) *ethutil.Value { - if int64(len(c.script)-1) < pc.Int64() { - return ethutil.NewValue(0) - } - - return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) -} - -func (c *Contract) SetMem(num *big.Int, val *ethutil.Value) { - addr := ethutil.BigToBytes(num, 256) - c.state.trie.Update(string(addr), string(val.Encode())) -} - -// Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *Contract) ReturnGas(val *big.Int, state *State) { - c.Amount.Add(c.Amount, val) -} - -func (c *Contract) Address() []byte { - return c.address -} - -func (c *Contract) Script() []byte { - return c.script -} - -func (c *Contract) Init() []byte { - return c.initScript -} - -func (c *Contract) RlpEncode() []byte { - return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root, c.script, c.initScript}) -} - -func (c *Contract) RlpDecode(data []byte) { - decoder := ethutil.NewValueFromBytes(data) - - c.Amount = decoder.Get(0).BigInt() - c.Nonce = decoder.Get(1).Uint() - c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) - c.script = decoder.Get(3).Bytes() - c.initScript = decoder.Get(4).Bytes() -} - -func MakeContract(tx *Transaction, state *State) *Contract { - // Create contract if there's no recipient - if tx.IsContract() { - addr := tx.Hash()[12:] - - value := tx.Value - contract := NewContract(addr, value, []byte("")) - state.trie.Update(string(addr), string(contract.RlpEncode())) - contract.script = tx.Data - contract.initScript = tx.Init - - /* - for i, val := range tx.Data { - if len(val) > 0 { - bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) - contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val))) - } - } - */ - state.trie.Update(string(addr), string(contract.RlpEncode())) - - return contract - } - - return nil -} diff --git a/ethchain/keypair.go b/ethchain/keypair.go index 9daaedbee..a5af791d0 100644 --- a/ethchain/keypair.go +++ b/ethchain/keypair.go @@ -10,7 +10,7 @@ type KeyPair struct { PublicKey []byte // The associated account - account *Account + account *StateObject state *State } @@ -24,7 +24,7 @@ func (k *KeyPair) Address() []byte { return ethutil.Sha3Bin(k.PublicKey[1:])[12:] } -func (k *KeyPair) Account() *Account { +func (k *KeyPair) Account() *StateObject { if k.account == nil { k.account = k.state.GetAccount(k.Address()) } diff --git a/ethchain/state.go b/ethchain/state.go index 1860647f2..655848932 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -47,23 +47,14 @@ func (s *State) Purge() int { return s.trie.NewIterator().Purge() } -func (s *State) GetContract(addr []byte) *Contract { +func (s *State) GetContract(addr []byte) *StateObject { data := s.trie.Get(string(addr)) if data == "" { return nil } - // Whet get contract is called the retrieved value might - // be an account. The StateManager uses this to check - // to see if the address a tx was sent to is a contract - // or an account - value := ethutil.NewValueFromBytes([]byte(data)) - if value.Len() == 2 { - return nil - } - // build contract - contract := NewContractFromBytes(addr, []byte(data)) + contract := NewStateObjectFromBytes(addr, []byte(data)) // Check if there's a cached state for this contract cachedState := s.states[string(addr)] @@ -77,28 +68,17 @@ func (s *State) GetContract(addr []byte) *Contract { return contract } -func (s *State) UpdateContract(contract *Contract) { - addr := contract.Address() - - s.states[string(addr)] = contract.state - s.trie.Update(string(addr), string(contract.RlpEncode())) -} - -func (s *State) GetAccount(addr []byte) (account *Account) { +func (s *State) GetAccount(addr []byte) (account *StateObject) { data := s.trie.Get(string(addr)) if data == "" { account = NewAccount(addr, big.NewInt(0)) } else { - account = NewAccountFromData(addr, []byte(data)) + account = NewStateObjectFromBytes(addr, []byte(data)) } return } -func (s *State) UpdateAccount(addr []byte, account *Account) { - s.trie.Update(string(addr), string(account.RlpEncode())) -} - func (s *State) Cmp(other *State) bool { return s.trie.Cmp(other.trie) } @@ -119,7 +99,7 @@ const ( // Returns the object stored at key and the type stored at key // Returns nil if nothing is stored -func (s *State) Get(key []byte) (*ethutil.Value, ObjType) { +func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) { // Fetch data from the trie data := s.trie.Get(string(key)) // Returns the nil type, indicating nothing could be retrieved. @@ -145,6 +125,17 @@ func (s *State) Get(key []byte) (*ethutil.Value, ObjType) { return val, typ } +// Updates any given state object +func (s *State) UpdateStateObject(object *StateObject) { + addr := object.Address() + + if object.state != nil { + s.states[string(addr)] = object.state + } + + s.trie.Update(string(addr), string(object.RlpEncode())) +} + func (s *State) Put(key, object []byte) { s.trie.Update(string(key), string(object)) } @@ -152,27 +143,3 @@ func (s *State) Put(key, object []byte) { func (s *State) Root() interface{} { return s.trie.Root } - -// Script compilation functions -// Compiles strings to machine code -func Compile(code []string) (script []string) { - script = make([]string, len(code)) - for i, val := range code { - instr, _ := ethutil.CompileInstr(val) - - script[i] = string(instr) - } - - return -} - -func CompileToValues(code []string) (script []*ethutil.Value) { - script = make([]*ethutil.Value, len(code)) - for i, val := range code { - instr, _ := ethutil.CompileInstr(val) - - script[i] = ethutil.NewValue(instr) - } - - return -} diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 549d59959..5e30d7280 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -30,7 +30,7 @@ type StateManager struct { bc *BlockChain // States for addresses. You can watch any address // at any given time - addrStateStore *AddrStateStore + stateObjectCache *StateObjectCache // Stack for processing contracts stack *Stack @@ -54,12 +54,12 @@ type StateManager struct { func NewStateManager(ethereum EthManager) *StateManager { sm := &StateManager{ - stack: NewStack(), - mem: make(map[string]*big.Int), - Pow: &EasyPow{}, - Ethereum: ethereum, - addrStateStore: NewAddrStateStore(), - bc: ethereum.BlockChain(), + stack: NewStack(), + mem: make(map[string]*big.Int), + Pow: &EasyPow{}, + Ethereum: ethereum, + stateObjectCache: NewStateObjectCache(), + bc: ethereum.BlockChain(), } sm.procState = ethereum.BlockChain().CurrentBlock.State() return sm @@ -70,18 +70,18 @@ func (sm *StateManager) ProcState() *State { } // Watches any given address and puts it in the address state store -func (sm *StateManager) WatchAddr(addr []byte) *AccountState { +func (sm *StateManager) WatchAddr(addr []byte) *CachedStateObject { //XXX account := sm.bc.CurrentBlock.state.GetAccount(addr) account := sm.procState.GetAccount(addr) - return sm.addrStateStore.Add(addr, account) + return sm.stateObjectCache.Add(addr, account) } -func (sm *StateManager) GetAddrState(addr []byte) *AccountState { - account := sm.addrStateStore.Get(addr) +func (sm *StateManager) GetAddrState(addr []byte) *CachedStateObject { + account := sm.stateObjectCache.Get(addr) if account == nil { a := sm.procState.GetAccount(addr) - account = &AccountState{Nonce: a.Nonce, Account: a} + account = &CachedStateObject{Nonce: a.Nonce, Object: a} } return account @@ -116,7 +116,7 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { if contract := sm.procState.GetContract(tx.Recipient); contract != nil { err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, true) if err == nil { - sm.ProcessContract(contract, tx, block) + sm.EvalScript(contract.Script(), contract, tx, block) } } else { err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) @@ -180,7 +180,6 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { return err } - // if !sm.compState.Cmp(sm.procState) if !sm.compState.Cmp(sm.procState) { return fmt.Errorf("Invalid merkle root. Expected %x, got %x", sm.compState.trie.Root, sm.procState.trie.Root) } @@ -190,9 +189,6 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { // Sync the current block's state to the database and cancelling out the deferred Undo sm.procState.Sync() - // Broadcast the valid block back to the wire - //sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val}) - // Add the block to the chain sm.bc.Add(block) @@ -282,22 +278,20 @@ func CalculateUncleReward(block *Block) *big.Int { } func (sm *StateManager) AccumelateRewards(block *Block) error { - - // Get the coinbase rlp data - acc := sm.procState.GetAccount(block.Coinbase) + // Get the account associated with the coinbase + account := sm.procState.GetAccount(block.Coinbase) // Reward amount of ether to the coinbase address - acc.AddFee(CalculateBlockReward(block, len(block.Uncles))) + account.AddAmount(CalculateBlockReward(block, len(block.Uncles))) addr := make([]byte, len(block.Coinbase)) copy(addr, block.Coinbase) - sm.procState.UpdateAccount(addr, acc) + sm.procState.UpdateStateObject(account) for _, uncle := range block.Uncles { - uncleAddr := sm.procState.GetAccount(uncle.Coinbase) - uncleAddr.AddFee(CalculateUncleReward(uncle)) + uncleAccount := sm.procState.GetAccount(uncle.Coinbase) + uncleAccount.AddAmount(CalculateUncleReward(uncle)) - //processor.state.UpdateAccount(uncle.Coinbase, uncleAddr) - sm.procState.UpdateAccount(uncle.Coinbase, uncleAddr) + sm.procState.UpdateStateObject(uncleAccount) } return nil @@ -307,7 +301,7 @@ func (sm *StateManager) Stop() { sm.bc.Stop() } -func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) { +func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) { // Recovering function in case the VM had any errors defer func() { if r := recover(); r != nil { @@ -316,7 +310,7 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo }() caller := sm.procState.GetAccount(tx.Sender()) - closure := NewClosure(caller, contract, contract.script, sm.procState, tx.Gas, tx.Value) + closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ Origin: caller.Address(), BlockNumber: block.BlockInfo().Number, @@ -324,11 +318,9 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo Coinbase: block.Coinbase, Time: block.Time, Diff: block.Difficulty, - // XXX Tx data? Could be just an argument to the closure instead - TxData: nil, }) closure.Call(vm, nil, nil) // Update the account (refunds) - sm.procState.UpdateAccount(tx.Sender(), caller) + sm.procState.UpdateStateObject(caller) } diff --git a/ethchain/state_object.go b/ethchain/state_object.go new file mode 100644 index 000000000..8b4de0c4f --- /dev/null +++ b/ethchain/state_object.go @@ -0,0 +1,162 @@ +package ethchain + +import ( + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type StateObject struct { + // Address of the object + address []byte + // Shared attributes + Amount *big.Int + Nonce uint64 + // Contract related attributes + state *State + script []byte + initScript []byte +} + +func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { + contract := &StateObject{address: address, Amount: Amount, Nonce: 0} + contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) + + return contract +} + +// Returns a newly created account +func NewAccount(address []byte, amount *big.Int) *StateObject { + account := &StateObject{address: address, Amount: amount, Nonce: 0} + + return account +} + +func NewStateObjectFromBytes(address, data []byte) *StateObject { + object := &StateObject{address: address} + object.RlpDecode(data) + + return object +} + +func (c *StateObject) Addr(addr []byte) *ethutil.Value { + return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) +} + +func (c *StateObject) SetAddr(addr []byte, value interface{}) { + c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) +} + +func (c *StateObject) State() *State { + return c.state +} + +func (c *StateObject) GetMem(num *big.Int) *ethutil.Value { + nb := ethutil.BigToBytes(num, 256) + + return c.Addr(nb) +} + +func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { + if int64(len(c.script)-1) < pc.Int64() { + return ethutil.NewValue(0) + } + + return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) +} + +func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { + addr := ethutil.BigToBytes(num, 256) + c.state.trie.Update(string(addr), string(val.Encode())) +} + +// Return the gas back to the origin. Used by the Virtual machine or Closures +func (c *StateObject) ReturnGas(val *big.Int, state *State) { + c.AddAmount(val) +} + +func (c *StateObject) AddAmount(amount *big.Int) { + c.Amount.Add(c.Amount, amount) +} + +func (c *StateObject) SubAmount(amount *big.Int) { + c.Amount.Sub(c.Amount, amount) +} + +func (c *StateObject) Address() []byte { + return c.address +} + +func (c *StateObject) Script() []byte { + return c.script +} + +func (c *StateObject) Init() []byte { + return c.initScript +} + +func (c *StateObject) RlpEncode() []byte { + var root interface{} + if c.state != nil { + root = c.state.trie.Root + } else { + root = nil + } + return ethutil.Encode([]interface{}{c.Amount, c.Nonce, root, c.script}) +} + +func (c *StateObject) RlpDecode(data []byte) { + decoder := ethutil.NewValueFromBytes(data) + + c.Amount = decoder.Get(0).BigInt() + c.Nonce = decoder.Get(1).Uint() + c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) + c.script = decoder.Get(3).Bytes() +} + +func MakeContract(tx *Transaction, state *State) *StateObject { + // Create contract if there's no recipient + if tx.IsContract() { + // FIXME + addr := tx.Hash()[12:] + + value := tx.Value + contract := NewContract(addr, value, []byte("")) + state.UpdateStateObject(contract) + + contract.script = tx.Data + contract.initScript = tx.Init + + state.UpdateStateObject(contract) + + return contract + } + + return nil +} + +// The cached state and state object cache are helpers which will give you somewhat +// control over the nonce. When creating new transactions you're interested in the 'next' +// nonce rather than the current nonce. This to avoid creating invalid-nonce transactions. +type StateObjectCache struct { + cachedObjects map[string]*CachedStateObject +} + +func NewStateObjectCache() *StateObjectCache { + return &StateObjectCache{cachedObjects: make(map[string]*CachedStateObject)} +} + +func (s *StateObjectCache) Add(addr []byte, object *StateObject) *CachedStateObject { + state := &CachedStateObject{Nonce: object.Nonce, Object: object} + s.cachedObjects[string(addr)] = state + + return state +} + +func (s *StateObjectCache) Get(addr []byte) *CachedStateObject { + return s.cachedObjects[string(addr)] +} + +type CachedStateObject struct { + Nonce uint64 + Object *StateObject +} diff --git a/ethchain/transaction.go b/ethchain/transaction.go index b359c9151..78044e840 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -23,8 +23,8 @@ type Transaction struct { contractCreation bool } -func NewContractCreationTx(value, gasprice *big.Int, data []byte) *Transaction { - return &Transaction{Value: value, Gasprice: gasprice, Data: data, contractCreation: true} +func NewContractCreationTx(value, gasprice *big.Int, script []byte, init []byte) *Transaction { + return &Transaction{Value: value, Gasprice: gasprice, Data: script, Init: init, contractCreation: true} } func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []byte) *Transaction { diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 0bcfe6923..5cdda17e2 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -118,20 +118,20 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract // Send Tx to self if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { // Subtract the fee - sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) + sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat)) } else if toContract { - sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) + sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat)) } else { // Subtract the amount from the senders account - sender.Amount.Sub(sender.Amount, totAmount) + sender.SubAmount(totAmount) // Add the amount to receivers account which should conclude this transaction - receiver.Amount.Add(receiver.Amount, tx.Value) + receiver.AddAmount(tx.Value) - block.state.UpdateAccount(tx.Recipient, receiver) + block.state.UpdateStateObject(receiver) } - block.state.UpdateAccount(tx.Sender(), sender) + block.state.UpdateStateObject(sender) log.Printf("[TXPL] Processed Tx %x\n", tx.Hash()) @@ -151,7 +151,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { // Get the sender accountState := pool.Ethereum.StateManager().GetAddrState(tx.Sender()) - sender := accountState.Account + sender := accountState.Object totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) // Make sure there's enough in the sender's account. Having insufficient