Running contracts fixed

This commit is contained in:
obscuren 2014-02-19 11:35:17 +01:00
parent c866fcc5b3
commit 24f2b2afc3
10 changed files with 120 additions and 98 deletions

View File

@ -46,6 +46,8 @@ type Block struct {
// List of transactions and/or contracts
transactions []*Transaction
TxSha []byte
contractStates map[string]*ethutil.Trie
}
// New block takes a raw encoded string
@ -87,6 +89,7 @@ func CreateBlock(root interface{},
Time: time.Now().Unix(),
Extra: extra,
UncleSha: EmptyShaList,
contractStates: make(map[string]*ethutil.Trie),
}
block.SetTransactions(txes)
block.SetUncles([]*Block{})
@ -131,6 +134,13 @@ func (block *Block) GetContract(addr []byte) *Contract {
contract := &Contract{}
contract.RlpDecode([]byte(data))
cachedState := block.contractStates[string(addr)]
if cachedState != nil {
contract.state = cachedState
} else {
block.contractStates[string(addr)] = contract.state
}
return contract
}
func (block *Block) UpdateContract(addr []byte, contract *Contract) {
@ -190,6 +200,25 @@ func (block *Block) BlockInfo() BlockInfo {
return bi
}
// Sync the block's state and contract respectively
func (block *Block) Sync() {
// Sync all contracts currently in cache
for _, val := range block.contractStates {
val.Sync()
}
// Sync the block state itself
block.state.Sync()
}
func (block *Block) Undo() {
// Sync all contracts currently in cache
for _, val := range block.contractStates {
val.Undo()
}
// Sync the block state itself
block.state.Undo()
}
func (block *Block) MakeContract(tx *Transaction) {
// Create contract if there's no recipient
if tx.IsContract() {
@ -199,9 +228,14 @@ func (block *Block) MakeContract(tx *Transaction) {
contract := NewContract(value, []byte(""))
block.state.Update(string(addr), string(contract.RlpEncode()))
for i, val := range tx.Data {
contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val)
if len(val) > 0 {
bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
contract.state.Update(string(bytNum), val)
}
}
block.UpdateContract(addr, contract)
block.contractStates[string(addr)] = contract.state
}
}
@ -288,6 +322,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
block.Time = int64(header.Get(6).BigInt().Uint64())
block.Extra = header.Get(7).Str()
block.Nonce = header.Get(8).Bytes()
block.contractStates = make(map[string]*ethutil.Trie)
// Tx list might be empty if this is an uncle. Uncles only have their
// header set.

View File

@ -107,7 +107,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
// we don't want to undo but since undo only happens on dirty
// nodes this won't happen because Commit would have been called
// before that.
defer bm.bc.CurrentBlock.State().Undo()
defer bm.bc.CurrentBlock.Undo()
hash := block.Hash()
@ -142,7 +142,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
// Calculate the new total difficulty and sync back to the db
if bm.CalculateTD(block) {
// Sync the current block's state to the database and cancelling out the deferred Undo
bm.bc.CurrentBlock.State().Sync()
bm.bc.CurrentBlock.Sync()
// Add the block to the chain
bm.bc.Add(block)
@ -280,11 +280,13 @@ func (bm *BlockManager) Stop() {
func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
// Recovering function in case the VM had any errors
/*
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from VM execution with err =", r)
}
}()
*/
// Process contract
bm.ProcContract(tx, block, func(opType OpType) bool {
@ -305,6 +307,7 @@ func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallbac
blockInfo := bm.bc.BlockInfo(block)
contract := block.GetContract(tx.Hash())
if contract == nil {
fmt.Println("Contract not found")
return
@ -313,7 +316,7 @@ func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallbac
Pow256 := ethutil.BigPow(2, 256)
if ethutil.Config.Debug {
fmt.Printf("# op arg\n")
fmt.Printf("# op\n")
}
out:
for {
@ -321,9 +324,11 @@ out:
base := new(big.Int)
// XXX Should Instr return big int slice instead of string slice?
// Get the next instruction from the contract
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
nb := ethutil.NumberToBytes(uint64(pc), 32)
o, _, _ := ethutil.Instr(contract.State().Get(string(nb)))
nb := ethutil.BigToBytes(big.NewInt(int64(pc)), 256)
r := contract.State().Get(string(nb))
v := ethutil.NewValueFromBytes([]byte(r))
//fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb)
o := v.Uint()
op := OpCode(o)
if !cb(0) {
@ -575,7 +580,10 @@ out:
case oSSTORE:
// Store Y at index X
x, y := bm.stack.Popn()
contract.State().Update(x.String(), string(ethutil.Encode(y)))
idx := ethutil.BigToBytes(x, 256)
val := ethutil.NewValue(y)
//fmt.Printf("STORING VALUE: %v @ %v\n", val.BigInt(), ethutil.BigD(idx))
contract.State().Update(string(idx), string(val.Encode()))
case oJMP:
x := int(bm.stack.Pop().Uint64())
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
@ -617,7 +625,10 @@ out:
bm.TransactionPool.QueueTransaction(tx)
case oSUICIDE:
//addr := bm.stack.Pop()
default:
fmt.Println("Invalid OPCODE", op)
}
//bm.stack.Print()
pc++
}
}

View File

@ -1,75 +1,29 @@
package ethchain
/*
import (
_ "fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"testing"
)
func TestVm(t *testing.T) {
InitFees()
ethutil.ReadConfig("")
db, _ := NewMemDatabase()
Db = db
db, _ := ethdb.NewMemDatabase()
ethutil.Config.Db = db
bm := NewBlockManager(nil)
ctrct := NewTransaction("", 200000000, []string{
"PUSH", "1a2f2e",
"PUSH", "hallo",
"POP", // POP hallo
"PUSH", "3",
"LOAD", // Load hallo back on the stack
"PUSH", "1",
"PUSH", "2",
"ADD",
"PUSH", "2",
"PUSH", "1",
"SUB",
"PUSH", "100000000000000000000000",
"PUSH", "10000000000000",
"SDIV",
"PUSH", "105",
"PUSH", "200",
"MOD",
"PUSH", "100000000000000000000000",
"PUSH", "10000000000000",
"SMOD",
"PUSH", "5",
"PUSH", "10",
"LT",
"PUSH", "5",
"PUSH", "5",
"LE",
"PUSH", "50",
"PUSH", "5",
"GT",
"PUSH", "5",
"PUSH", "5",
"GE",
"PUSH", "10",
"PUSH", "10",
"NOT",
"MYADDRESS",
"TXSENDER",
block := bm.bc.genesisBlock
ctrct := NewTransaction(ContractAddr, big.NewInt(200000000), []string{
"PUSH",
"1",
"PUSH",
"2",
"STOP",
})
tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
db.Put(block.Hash(), block.RlpEncode())
bm := NewBlockManager()
bm.ProcessBlock(block)
bm.ApplyTransactions(block, []*Transaction{ctrct})
}
*/

View File

@ -163,5 +163,12 @@ func (st *Stack) Push(d *big.Int) {
st.data = append(st.data, d)
}
func (st *Stack) Print() {
fmt.Println(st.data)
fmt.Println("# val (STACK)")
if len(st.data) > 0 {
for i, val := range st.data {
fmt.Printf("%-3d %v\n", i, val)
}
} else {
fmt.Println("-- empty --")
}
}

View File

@ -1,11 +1,14 @@
package ethchain
import (
"bytes"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
"math/big"
)
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
type Transaction struct {
Nonce uint64
Recipient []byte
@ -65,7 +68,7 @@ func (tx *Transaction) Hash() []byte {
}
func (tx *Transaction) IsContract() bool {
return len(tx.Recipient) == 0
return bytes.Compare(tx.Recipient, ContractAddr) == 0
}
func (tx *Transaction) Signature(key []byte) []byte {

View File

@ -2,8 +2,6 @@ package ethchain
import (
"encoding/hex"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"testing"
)
@ -42,6 +40,7 @@ func TestAddressRetrieval2(t *testing.T) {
tx.Sign(key)
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
//tx := NewTransactionFromData(data)
/*
fmt.Println(tx.RlpValue())
fmt.Printf("rlp %x\n", tx.RlpEncode())
@ -51,4 +50,5 @@ func TestAddressRetrieval2(t *testing.T) {
fmt.Printf("hex tx key %x\n", tx.PublicKey())
fmt.Printf("seder %x\n", tx.Sender())
*/
}

View File

@ -252,7 +252,7 @@ func (s *Ethereum) Start() {
if ethutil.Config.Seed {
log.Println("Seeding")
// Testnet seed bootstrapping
resp, err := http.Get("http://www.ethereum.org/servers.poc2.txt")
resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt")
if err != nil {
log.Println("Fetching seed failed:", err)
return

View File

@ -35,3 +35,9 @@ func BigD(data []byte) *big.Int {
return n
}
func BigToBytes(num *big.Int, base int) []byte {
ret := make([]byte, base/8)
return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
}

View File

@ -34,6 +34,7 @@ func ReadConfig(base string) *config {
usr, _ := user.Current()
path := path.Join(usr.HomeDir, base)
if len(base) > 0 {
//Check if the logging directory already exists, create it if not
_, err := os.Stat(path)
if err != nil {
@ -42,6 +43,7 @@ func ReadConfig(base string) *config {
os.Mkdir(path, 0777)
}
}
}
Config = &config{ExecPath: path, Debug: true, Ver: "0.2.3"}
Config.Log = NewLogger(LogFile|LogStd, 0)

View File

@ -119,6 +119,10 @@ func (t *Trie) Undo() {
t.Root = t.prevRoot
}
func (t *Trie) Cache() *Cache {
return t.cache
}
/*
* Public (query) interface functions
*/