diff --git a/account/account.go b/account/account.go index 520afd70c..235da8583 100644 --- a/account/account.go +++ b/account/account.go @@ -6,6 +6,7 @@ import ( "io" "github.com/tendermint/tendermint/binary" + "github.com/tendermint/tendermint/merkle" ) // Signable is an interface for all signable things. @@ -24,6 +25,11 @@ func SignBytes(o Signable) []byte { return buf.Bytes() } +// HashSignBytes is a convenience method for getting the hash of the bytes of a signable +func HashSignBytes(o Signable) []byte { + return merkle.HashFromBinary(SignBytes(o)) +} + //----------------------------------------------------------------------------- // Account resides in the application state, and is mutated by transactions diff --git a/consensus/state.go b/consensus/state.go index a9db89a5c..0b698a311 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -445,6 +445,7 @@ ACTION_LOOP: // cs.Step is at RoundStepNewHeight or RoundStepNewRound. newBlock := cs.blockStore.LoadBlock(cs.state.LastBlockHeight) cs.evsw.FireEvent(types.EventStringNewBlock(), newBlock) + // TODO: go fire events from event cache scheduleNextAction() continue ACTION_LOOP } else { diff --git a/rpc/handlers.go b/rpc/handlers.go index 37d817ef8..d3b969189 100644 --- a/rpc/handlers.go +++ b/rpc/handlers.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "net/http" "reflect" + "sync/atomic" "time" ) @@ -25,8 +26,8 @@ func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc) { func RegisterEventsHandler(mux *http.ServeMux, evsw *events.EventSwitch) { // websocket endpoint - w := NewWebsocketManager(evsw) - mux.HandleFunc("/events", w.websocketHandler) // websocket.Handler(w.eventsHandler)) + wm := NewWebsocketManager(evsw) + mux.HandleFunc("/events", wm.websocketHandler) // websocket.Handler(w.eventsHandler)) } //------------------------------------- @@ -193,7 +194,7 @@ func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) { const ( WSConnectionReaperSeconds = 5 - MaxFailedSendsSeconds = 10 + MaxFailedSends = 10 WriteChanBufferSize = 10 ) @@ -214,103 +215,76 @@ type WSResponse struct { // contains the listeners id type Connection struct { id string - wsCon *websocket.Conn + wsConn *websocket.Conn writeChan chan WSResponse - quitChan chan struct{} failedSends uint + started uint32 + stopped uint32 + + evsw *events.EventSwitch } // new websocket connection wrapper -func NewConnection(con *websocket.Conn) *Connection { +func NewConnection(wsConn *websocket.Conn) *Connection { return &Connection{ - id: con.RemoteAddr().String(), - wsCon: con, + id: wsConn.RemoteAddr().String(), + wsConn: wsConn, writeChan: make(chan WSResponse, WriteChanBufferSize), // buffered. we keep track when its full } } -// close the connection -func (c *Connection) Close() { - c.wsCon.Close() - close(c.writeChan) - close(c.quitChan) -} +// start the connection and hand her the event switch +func (con *Connection) Start(evsw *events.EventSwitch) { + if atomic.CompareAndSwapUint32(&con.started, 0, 1) { + con.evsw = evsw -// main manager for all websocket connections -// holds the event switch -type WebsocketManager struct { - websocket.Upgrader - ew *events.EventSwitch - cons map[string]*Connection -} - -func NewWebsocketManager(ew *events.EventSwitch) *WebsocketManager { - return &WebsocketManager{ - ew: ew, - cons: make(map[string]*Connection), - Upgrader: websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { - // TODO - return true - }, - }, + // read subscriptions/unsubscriptions to events + go con.read() + // write responses + con.write() } } -func (wm *WebsocketManager) websocketHandler(w http.ResponseWriter, r *http.Request) { - conn, err := wm.Upgrade(w, r, nil) - if err != nil { - // TODO - log.Error("Failed to upgrade to websocket connection", "error", err) - return +// close the connection +func (con *Connection) Stop() { + if atomic.CompareAndSwapUint32(&con.stopped, 0, 1) { + con.wsConn.Close() + close(con.writeChan) } - wm.handleWebsocket(conn) - -} - -func (w *WebsocketManager) handleWebsocket(con *websocket.Conn) { - // register connection - c := NewConnection(con) - w.cons[c.id] = c - log.Info("New websocket connection", "origin", c.id) - - // read subscriptions/unsubscriptions to events - go w.read(c) - // write responses - w.write(c) } // read from the socket and subscribe to or unsubscribe from events -func (w *WebsocketManager) read(con *Connection) { +func (con *Connection) read() { reaper := time.Tick(time.Second * WSConnectionReaperSeconds) for { select { case <-reaper: - if con.failedSends > MaxFailedSendsSeconds { + if con.failedSends > MaxFailedSends { // sending has failed too many times. // kill the connection - con.quitChan <- struct{}{} + con.Stop() + return } default: var in []byte - _, in, err := con.wsCon.ReadMessage() + _, in, err := con.wsConn.ReadMessage() if err != nil { // an error reading the connection, - // so kill the connection - con.quitChan <- struct{}{} + // kill the connection + con.Stop() + return } var req WSRequest err = json.Unmarshal(in, &req) if err != nil { errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error()) con.writeChan <- WSResponse{Error: errStr} + continue } switch req.Type { case "subscribe": log.Info("New event subscription", "con id", con.id, "event", req.Event) - w.ew.AddListenerForEvent(con.id, req.Event, func(msg interface{}) { + con.evsw.AddListenerForEvent(con.id, req.Event, func(msg interface{}) { resp := WSResponse{ Event: req.Event, Data: msg, @@ -328,9 +302,9 @@ func (w *WebsocketManager) read(con *Connection) { }) case "unsubscribe": if req.Event != "" { - w.ew.RemoveListenerForEvent(req.Event, con.id) + con.evsw.RemoveListenerForEvent(req.Event, con.id) } else { - w.ew.RemoveListener(con.id) + con.evsw.RemoveListener(con.id) } default: con.writeChan <- WSResponse{Error: "Unknown request type: " + req.Type} @@ -340,33 +314,64 @@ func (w *WebsocketManager) read(con *Connection) { } } -// receives on a write channel and writes out to the socket -func (w *WebsocketManager) write(con *Connection) { +// receives on a write channel and writes out on the socket +func (con *Connection) write() { n, err := new(int64), new(error) for { - select { - case msg := <-con.writeChan: - buf := new(bytes.Buffer) - binary.WriteJSON(msg, buf, n, err) - if *err != nil { - log.Error("Failed to write JSON WSResponse", "error", err) - } else { - //websocket.Message.Send(con.wsCon, buf.Bytes()) - if err := con.wsCon.WriteMessage(websocket.TextMessage, buf.Bytes()); err != nil { - log.Error("Failed to write response on websocket", "error", err) - } - } - case <-con.quitChan: - w.closeConn(con) + msg, more := <-con.writeChan + if !more { + // the channel was closed, so ensure + // connection is stopped and return + con.Stop() return } + buf := new(bytes.Buffer) + binary.WriteJSON(msg, buf, n, err) + if *err != nil { + log.Error("Failed to write JSON WSResponse", "error", err) + } else { + if err := con.wsConn.WriteMessage(websocket.TextMessage, buf.Bytes()); err != nil { + log.Error("Failed to write response on websocket", "error", err) + con.Stop() + return + } + } } } -// close a connection and delete from manager -func (w *WebsocketManager) closeConn(con *Connection) { - con.Close() - delete(w.cons, con.id) +// main manager for all websocket connections +// holds the event switch +type WebsocketManager struct { + websocket.Upgrader + evsw *events.EventSwitch +} + +func NewWebsocketManager(evsw *events.EventSwitch) *WebsocketManager { + return &WebsocketManager{ + evsw: evsw, + Upgrader: websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + // TODO + return true + }, + }, + } +} + +func (wm *WebsocketManager) websocketHandler(w http.ResponseWriter, r *http.Request) { + wsConn, err := wm.Upgrade(w, r, nil) + if err != nil { + // TODO - return http error + log.Error("Failed to upgrade to websocket connection", "error", err) + return + } + + // register connection + con := NewConnection(wsConn) + log.Info("New websocket connection", "origin", con.id) + con.Start(wm.evsw) } // rpc.websocket diff --git a/rpc/http_server.go b/rpc/http_server.go index 3b09ac373..1cb79bc6d 100644 --- a/rpc/http_server.go +++ b/rpc/http_server.go @@ -53,7 +53,7 @@ func WriteRPCResponse(w http.ResponseWriter, res RPCResponse) { func RecoverAndLogHandler(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Wrap the ResponseWriter to remember the status - rww := &ResponseWriterWrapper{-1, w, w.(http.Hijacker)} + rww := &ResponseWriterWrapper{-1, w} begin := time.Now() // Common headers @@ -100,7 +100,6 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler { type ResponseWriterWrapper struct { Status int http.ResponseWriter - hj http.Hijacker // necessary for websocket upgrades } func (w *ResponseWriterWrapper) WriteHeader(status int) { @@ -110,7 +109,7 @@ func (w *ResponseWriterWrapper) WriteHeader(status int) { // implements http.Hijacker func (w *ResponseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return w.hj.Hijack() + return w.ResponseWriter.(http.Hijacker).Hijack() } // Stick it as a deferred statement in gouroutines to prevent the program from crashing. diff --git a/rpc/test/client_ws_test.go b/rpc/test/client_ws_test.go index b8ae53123..b342d390e 100644 --- a/rpc/test/client_ws_test.go +++ b/rpc/test/client_ws_test.go @@ -1,7 +1,6 @@ package rpc import ( - "encoding/hex" "encoding/json" "fmt" "github.com/gorilla/websocket" @@ -172,7 +171,7 @@ func TestWSSend(t *testing.T) { amt := uint64(100) con := newWSCon(t) - eidInput := types.EventStringAccInput(byteAddr) + eidInput := types.EventStringAccInput(userByteAddr) eidOutput := types.EventStringAccOutput(toAddr) subscribe(t, con, eidInput) subscribe(t, con, eidOutput) @@ -182,7 +181,7 @@ func TestWSSend(t *testing.T) { con.Close() }() waitForEvent(t, con, eidInput, true, func() { - broadcastTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) + broadcastTx(t, "JSONRPC", userByteAddr, toAddr, nil, userBytePriv, amt, 0, 0) }, unmarshalValidateSend(amt, toAddr)) waitForEvent(t, con, eidOutput, true, func() {}, unmarshalValidateSend(amt, toAddr)) } @@ -190,7 +189,7 @@ func TestWSSend(t *testing.T) { // ensure events are only fired once for a given transaction func TestWSDoubleFire(t *testing.T) { con := newWSCon(t) - eid := types.EventStringAccInput(byteAddr) + eid := types.EventStringAccInput(userByteAddr) subscribe(t, con, eid) defer func() { unsubscribe(t, con, eid) @@ -200,7 +199,7 @@ func TestWSDoubleFire(t *testing.T) { toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54} // broadcast the transaction, wait to hear about it waitForEvent(t, con, eid, true, func() { - broadcastTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) + broadcastTx(t, "JSONRPC", userByteAddr, toAddr, nil, userBytePriv, amt, 0, 0) }, func(eid string, b []byte) error { return nil }) @@ -213,9 +212,8 @@ func TestWSDoubleFire(t *testing.T) { // create a contract, wait for the event, and send it a msg, validate the return func TestWSCallWait(t *testing.T) { - byteAddr, _ := hex.DecodeString(userAddr) con := newWSCon(t) - eid1 := types.EventStringAccInput(byteAddr) + eid1 := types.EventStringAccInput(userByteAddr) subscribe(t, con, eid1) defer func() { unsubscribe(t, con, eid1) @@ -226,7 +224,7 @@ func TestWSCallWait(t *testing.T) { var contractAddr []byte // wait for the contract to be created waitForEvent(t, con, eid1, true, func() { - _, receipt := broadcastTx(t, "JSONRPC", byteAddr, nil, code, byteKey, amt, 1000, 1000) + _, receipt := broadcastTx(t, "JSONRPC", userByteAddr, nil, code, userBytePriv, amt, 1000, 1000) contractAddr = receipt.ContractAddr }, unmarshalValidateCall(amt, returnCode)) @@ -241,19 +239,18 @@ func TestWSCallWait(t *testing.T) { // get the return value from a call data := []byte{0x1} // just needs to be non empty for this to be a CallTx waitForEvent(t, con, eid2, true, func() { - broadcastTx(t, "JSONRPC", byteAddr, contractAddr, data, byteKey, amt, 1000, 1000) + broadcastTx(t, "JSONRPC", userByteAddr, contractAddr, data, userBytePriv, amt, 1000, 1000) }, unmarshalValidateCall(amt, returnVal)) } // create a contract and send it a msg without waiting. wait for contract event // and validate return func TestWSCallNoWait(t *testing.T) { - byteAddr, _ := hex.DecodeString(userAddr) con := newWSCon(t) amt := uint64(10000) code, _, returnVal := simpleContract() - _, receipt := broadcastTx(t, "JSONRPC", byteAddr, nil, code, byteKey, amt, 1000, 1000) + _, receipt := broadcastTx(t, "JSONRPC", userByteAddr, nil, code, userBytePriv, amt, 1000, 1000) contractAddr := receipt.ContractAddr // susbscribe to the new contract @@ -267,23 +264,22 @@ func TestWSCallNoWait(t *testing.T) { // get the return value from a call data := []byte{0x1} // just needs to be non empty for this to be a CallTx waitForEvent(t, con, eid, true, func() { - broadcastTx(t, "JSONRPC", byteAddr, contractAddr, data, byteKey, amt, 1000, 1000) + broadcastTx(t, "JSONRPC", userByteAddr, contractAddr, data, userBytePriv, amt, 1000, 1000) }, unmarshalValidateCall(amt, returnVal)) } // create two contracts, one of which calls the other func TestWSCallCall(t *testing.T) { - byteAddr, _ := hex.DecodeString(userAddr) con := newWSCon(t) amt := uint64(10000) code, _, returnVal := simpleContract() txid := new([]byte) // deploy the two contracts - _, receipt := broadcastTx(t, "JSONRPC", byteAddr, nil, code, byteKey, amt, 1000, 1000) + _, receipt := broadcastTx(t, "JSONRPC", userByteAddr, nil, code, userBytePriv, amt, 1000, 1000) contractAddr1 := receipt.ContractAddr code, _, _ = simpleCallContract(contractAddr1) - _, receipt = broadcastTx(t, "JSONRPC", byteAddr, nil, code, byteKey, amt, 1000, 1000) + _, receipt = broadcastTx(t, "JSONRPC", userByteAddr, nil, code, userBytePriv, amt, 1000, 1000) contractAddr2 := receipt.ContractAddr // susbscribe to the new contracts @@ -300,7 +296,7 @@ func TestWSCallCall(t *testing.T) { // call contract2, which should call contract1, and wait for ev1 data := []byte{0x1} // just needs to be non empty for this to be a CallTx waitForEvent(t, con, eid1, true, func() { - tx, _ := broadcastTx(t, "JSONRPC", byteAddr, contractAddr2, data, byteKey, amt, 1000, 1000) - *txid = account.SignBytes(tx) - }, unmarshalValidateCallCall(byteAddr, returnVal, txid)) + tx, _ := broadcastTx(t, "JSONRPC", userByteAddr, contractAddr2, data, userBytePriv, amt, 1000, 1000) + *txid = account.HashSignBytes(tx) + }, unmarshalValidateCallCall(userByteAddr, returnVal, txid)) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 9cb481afb..05d863d53 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -28,10 +28,10 @@ var ( mempoolCount = 0 - userAddr = "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB" - userPriv = "FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" - userPub = "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" - byteAddr, byteKey = initUserBytes() + userAddr = "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB" + userPriv = "FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" + userPub = "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" + userByteAddr, userBytePriv = initUserBytes() clients = map[string]cclient.Client{ "JSONRPC": cclient.NewClient(requestAddr, "JSONRPC"), @@ -39,11 +39,13 @@ var ( } ) +// returns byte versions of address and private key +// type [64]byte needed by account.GenPrivAccountFromKey func initUserBytes() ([]byte, [64]byte) { byteAddr, _ := hex.DecodeString(userAddr) var byteKey [64]byte - oh, _ := hex.DecodeString(userPriv) - copy(byteKey[:], oh) + userPrivByteSlice, _ := hex.DecodeString(userPriv) + copy(byteKey[:], userPrivByteSlice) return byteAddr, byteKey } diff --git a/rpc/test/tests.go b/rpc/test/tests.go index 35a2b70c1..f95a42d4b 100644 --- a/rpc/test/tests.go +++ b/rpc/test/tests.go @@ -2,14 +2,12 @@ package rpc import ( "bytes" - "encoding/hex" "fmt" . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" "testing" - //"time" ) func testStatus(t *testing.T, typ string) { @@ -38,35 +36,34 @@ func testGenPriv(t *testing.T, typ string) { } func testGetAccount(t *testing.T, typ string) { - byteAddr, _ := hex.DecodeString(userAddr) - acc := getAccount(t, typ, byteAddr) + acc := getAccount(t, typ, userByteAddr) if acc == nil { t.Fatalf("Account was nil") } - if bytes.Compare(acc.Address, byteAddr) != 0 { - t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address, byteAddr) + if bytes.Compare(acc.Address, userByteAddr) != 0 { + t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address, userByteAddr) } } func testSignedTx(t *testing.T, typ string) { amt := uint64(100) toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54} - tx, priv := signTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) + tx, priv := signTx(t, typ, userByteAddr, toAddr, nil, userBytePriv, amt, 0, 0) + checkTx(t, userByteAddr, priv, tx.(*types.SendTx)) toAddr = []byte{20, 143, 24, 63, 16, 17, 83, 29, 90, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} - tx, priv = signTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) + tx, priv = signTx(t, typ, userByteAddr, toAddr, nil, userBytePriv, amt, 0, 0) + checkTx(t, userByteAddr, priv, tx.(*types.SendTx)) toAddr = []byte{0, 0, 4, 0, 0, 4, 0, 0, 4, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} - tx, priv = signTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) + tx, priv = signTx(t, typ, userByteAddr, toAddr, nil, userBytePriv, amt, 0, 0) + checkTx(t, userByteAddr, priv, tx.(*types.SendTx)) } func testBroadcastTx(t *testing.T, typ string) { amt := uint64(100) toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54} - tx, receipt := broadcastTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) + tx, receipt := broadcastTx(t, typ, userByteAddr, toAddr, nil, userBytePriv, amt, 0, 0) if receipt.CreatesContract > 0 { t.Fatal("This tx does not create a contract") } @@ -102,7 +99,7 @@ func testGetStorage(t *testing.T, typ string) { amt := uint64(1100) code := []byte{0x60, 0x5, 0x60, 0x1, 0x55} - _, receipt := broadcastTx(t, typ, byteAddr, nil, code, byteKey, amt, 1000, 1000) + _, receipt := broadcastTx(t, typ, userByteAddr, nil, code, userBytePriv, amt, 1000, 1000) if receipt.CreatesContract == 0 { t.Fatal("This tx creates a contract") } @@ -115,7 +112,6 @@ func testGetStorage(t *testing.T, typ string) { } // allow it to get mined - //time.Sleep(time.Second * 20) waitForEvent(t, con, eid, true, func() { }, func(eid string, b []byte) error { return nil @@ -160,7 +156,7 @@ func testCall(t *testing.T, typ string) { // create the contract amt := uint64(6969) code, _, _ := simpleContract() - _, receipt := broadcastTx(t, typ, byteAddr, nil, code, byteKey, amt, 1000, 1000) + _, receipt := broadcastTx(t, typ, userByteAddr, nil, code, userBytePriv, amt, 1000, 1000) if receipt.CreatesContract == 0 { t.Fatal("This tx creates a contract") } @@ -173,7 +169,6 @@ func testCall(t *testing.T, typ string) { } // allow it to get mined - //time.Sleep(time.Second * 20) waitForEvent(t, con, eid, true, func() { }, func(eid string, b []byte) error { return nil diff --git a/rpc/test/ws_helpers.go b/rpc/test/ws_helpers.go index 77dd059f9..880220a32 100644 --- a/rpc/test/ws_helpers.go +++ b/rpc/test/ws_helpers.go @@ -6,7 +6,6 @@ import ( "github.com/gorilla/websocket" "github.com/tendermint/tendermint/binary" "github.com/tendermint/tendermint/types" - "github.com/tendermint/tendermint/vm" "testing" ) @@ -51,14 +50,14 @@ func unmarshalValidateSend(amt uint64, toAddr []byte) func(string, []byte) error return fmt.Errorf("Eventid is not correct. Got %s, expected %s", response.Event, eid) } tx := response.Data - if bytes.Compare(tx.Inputs[0].Address, byteAddr) != 0 { - return fmt.Errorf("Senders do not match up! Got %x, expected %x", tx.Inputs[0].Address, byteAddr) + if bytes.Compare(tx.Inputs[0].Address, userByteAddr) != 0 { + return fmt.Errorf("Senders do not match up! Got %x, expected %x", tx.Inputs[0].Address, userByteAddr) } if tx.Inputs[0].Amount != amt { return fmt.Errorf("Amt does not match up! Got %d, expected %d", tx.Inputs[0].Amount, amt) } if bytes.Compare(tx.Outputs[0].Address, toAddr) != 0 { - return fmt.Errorf("Receivers do not match up! Got %x, expected %x", tx.Outputs[0].Address, byteAddr) + return fmt.Errorf("Receivers do not match up! Got %x, expected %x", tx.Outputs[0].Address, userByteAddr) } return nil } @@ -88,8 +87,8 @@ func unmarshalValidateCall(amt uint64, returnCode []byte) func(string, []byte) e return fmt.Errorf(response.Data.Exception) } tx := response.Data.Tx - if bytes.Compare(tx.Input.Address, byteAddr) != 0 { - return fmt.Errorf("Senders do not match up! Got %x, expected %x", tx.Input.Address, byteAddr) + if bytes.Compare(tx.Input.Address, userByteAddr) != 0 { + return fmt.Errorf("Senders do not match up! Got %x, expected %x", tx.Input.Address, userByteAddr) } if tx.Input.Amount != amt { return fmt.Errorf("Amt does not match up! Got %d, expected %d", tx.Input.Amount, amt) @@ -107,13 +106,7 @@ func unmarshalValidateCallCall(origin, returnCode []byte, txid *[]byte) func(str // unmarshall and assert somethings var response struct { Event string - Data struct { - CallData *vm.CallData - Origin []byte - TxId []byte - Return []byte - Exception string - } + Data types.EventMsgCall Error string } var err error diff --git a/state/block_cache.go b/state/block_cache.go index c9e0348bf..1fc09f1ac 100644 --- a/state/block_cache.go +++ b/state/block_cache.go @@ -53,9 +53,7 @@ func (cache *BlockCache) GetAccount(addr []byte) *ac.Account { return acc } else { acc = cache.backend.GetAccount(addr) - if acc != nil { - cache.accounts[string(addr)] = accountInfo{acc, nil, false, false} - } + cache.accounts[string(addr)] = accountInfo{acc, nil, false, false} return acc } } @@ -189,7 +187,7 @@ func (cache *BlockCache) Sync() { } } else { if acc == nil { - panic(Fmt("Account should not be nil for addr: %x", addrStr)) + continue } if storage != nil { newStorageRoot := storage.Save() diff --git a/state/execution.go b/state/execution.go index 1a89fecce..918e9122b 100644 --- a/state/execution.go +++ b/state/execution.go @@ -426,12 +426,12 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall, fireEvents bool) erro txCache.UpdateAccount(caller) // because we adjusted by input above, and bumped nonce maybe. txCache.UpdateAccount(callee) // because we adjusted by input above. - vmach := vm.NewVM(txCache, params, caller.Address, account.SignBytes(tx)) + vmach := vm.NewVM(txCache, params, caller.Address, account.HashSignBytes(tx)) vmach.SetEventSwitch(_s.evsw) // NOTE: Call() transfers the value from caller to callee iff call succeeds. ret, err := vmach.Call(caller, callee, code, tx.Data, value, &gas) - var exception string + exception := "" if err != nil { exception = err.Error() // Failure. Charge the gas fee. The 'value' was otherwise not transferred. @@ -440,7 +440,6 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall, fireEvents bool) erro blockCache.UpdateAccount(inAcc) // Throw away 'txCache' which holds incomplete updates (don't sync it). } else { - exception = "" log.Debug("Successful execution") // Success if createAccount { @@ -455,17 +454,9 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall, fireEvents bool) erro if fireEvents { // Fire Events for sender and receiver // a separate event will be fired from vm for each - _s.evsw.FireEvent(types.EventStringAccInput(tx.Input.Address), struct { - Tx types.Tx - Return []byte - Exception string - }{tx, ret, exception}) - - _s.evsw.FireEvent(types.EventStringAccReceive(tx.Address), struct { - Tx types.Tx - Return []byte - Exception string - }{tx, ret, exception}) + _s.evsw.FireEvent(types.EventStringAccInput(tx.Input.Address), types.EventMsgCallTx{tx, ret, exception}) + + _s.evsw.FireEvent(types.EventStringAccReceive(tx.Address), types.EventMsgCallTx{tx, ret, exception}) } } else { // The mempool does not call txs until diff --git a/types/events.go b/types/events.go index 28965ecc9..0345efcb1 100644 --- a/types/events.go +++ b/types/events.go @@ -4,6 +4,8 @@ import ( "fmt" ) +// Functions to generate eventId strings + func EventStringAccInput(addr []byte) string { return fmt.Sprintf("Acc/%x/Input", addr) } @@ -40,6 +42,31 @@ func EventStringFork() string { return "Fork" } +// Most event messages are basic types (a block, a transaction) +// but some (an input to a call tx or a receive) are more exotic: + +type EventMsgCallTx struct { + Tx Tx + Return []byte + Exception string +} + +type CallData struct { + Caller []byte + Callee []byte + Data []byte + Value uint64 + Gas uint64 +} + +type EventMsgCall struct { + CallData *CallData + Origin []byte + TxId []byte + Return []byte + Exception string +} + /* Acc/XYZ/Input -> full tx or {full tx, return value, exception} Acc/XYZ/Output -> full tx diff --git a/vm/types.go b/vm/types.go index 3d7b9239e..443b72c63 100644 --- a/vm/types.go +++ b/vm/types.go @@ -46,11 +46,3 @@ type Params struct { BlockTime int64 GasLimit uint64 } - -type CallData struct { - Caller []byte - Callee []byte - Data []byte - Value uint64 - Gas uint64 -} diff --git a/vm/vm.go b/vm/vm.go index fe184f212..50b95a19d 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -91,17 +91,11 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga panic("Could not return value to caller") } } - // if callDepth is 0 the event is fired from ExecTx (along with the Input invent) + // if callDepth is 0 the event is fired from ExecTx (along with the Input event) // otherwise, we fire from here. if vm.callDepth != 0 && vm.evsw != nil { - vm.evsw.FireEvent(types.EventStringAccReceive(callee.Address.Prefix(20)), struct { - CallData *CallData - Origin []byte - TxId []byte - Return []byte - Exception string - }{ - &CallData{caller.Address.Prefix(20), callee.Address.Prefix(20), input, value, *gas}, + vm.evsw.FireEvent(types.EventStringAccReceive(callee.Address.Prefix(20)), types.EventMsgCall{ + &types.CallData{caller.Address.Prefix(20), callee.Address.Prefix(20), input, value, *gas}, vm.origin.Prefix(20), vm.txid, output,