aboutsummaryrefslogtreecommitdiffstats
path: root/ethchain/state_object.go
diff options
context:
space:
mode:
Diffstat (limited to 'ethchain/state_object.go')
-rw-r--r--ethchain/state_object.go194
1 files changed, 194 insertions, 0 deletions
diff --git a/ethchain/state_object.go b/ethchain/state_object.go
new file mode 100644
index 000000000..617646077
--- /dev/null
+++ b/ethchain/state_object.go
@@ -0,0 +1,194 @@
+package ethchain
+
+import (
+ "fmt"
+ "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
+}
+
+// Converts an transaction in to a state object
+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
+}
+
+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) State() *State {
+ return c.state
+}
+
+func (c *StateObject) N() *big.Int {
+ return big.NewInt(int64(c.Nonce))
+}
+
+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) SetMem(num *big.Int, val *ethutil.Value) {
+ addr := ethutil.BigToBytes(num, 256)
+ c.SetAddr(addr, val)
+}
+
+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()]})
+}
+
+// Return the gas back to the origin. Used by the Virtual machine or Closures
+func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
+ remainder := new(big.Int).Mul(gas, price)
+ c.AddAmount(remainder)
+}
+
+func (c *StateObject) AddAmount(amount *big.Int) {
+ c.SetAmount(new(big.Int).Add(c.Amount, amount))
+}
+
+func (c *StateObject) SubAmount(amount *big.Int) {
+ c.SetAmount(new(big.Int).Sub(c.Amount, amount))
+}
+
+func (c *StateObject) SetAmount(amount *big.Int) {
+ c.Amount = amount
+}
+
+func (c *StateObject) ConvertGas(gas, price *big.Int) error {
+ total := new(big.Int).Mul(gas, price)
+ if total.Cmp(c.Amount) > 0 {
+ return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total)
+ }
+
+ c.SubAmount(total)
+
+ return nil
+}
+
+// Returns the address of the contract/account
+func (c *StateObject) Address() []byte {
+ return c.address
+}
+
+// Returns the main script body
+func (c *StateObject) Script() []byte {
+ return c.script
+}
+
+// Returns the initialization script
+func (c *StateObject) Init() []byte {
+ return c.initScript
+}
+
+// State object encoding methods
+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()
+}
+
+// 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
+}
+
+type StorageState struct {
+ StateAddress []byte
+ Address []byte
+ Value *big.Int
+}