diff --git a/consensus/state.go b/consensus/state.go index ede0487a..a9db89a5 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -73,11 +73,14 @@ import ( ) const ( - roundDuration0 = 10 * time.Second // The first round is 60 seconds long. - roundDurationDelta = 3 * time.Second // Each successive round lasts 15 seconds longer. roundDeadlinePrevote = float64(1.0 / 3.0) // When the prevote is due. roundDeadlinePrecommit = float64(2.0 / 3.0) // When the precommit vote is due. - newHeightDelta = roundDuration0 / 3 // The time to wait between commitTime and startTime of next consensus rounds. +) + +var ( + RoundDuration0 = 10 * time.Second // The first round is 60 seconds long. + RoundDurationDelta = 3 * time.Second // Each successive round lasts 15 seconds longer. + newHeightDelta = RoundDuration0 / 3 // The time to wait between commitTime and startTime of next consensus rounds. ) var ( @@ -318,14 +321,14 @@ func (cs *ConsensusState) stepTransitionRoutine() { // NOTE: We can push directly to runActionCh because // we're running in a separate goroutine, which avoids deadlocks. rs := cs.getRoundState() - round, roundStartTime, roundDuration, _, elapsedRatio := calcRoundInfo(rs.StartTime) + round, roundStartTime, RoundDuration, _, elapsedRatio := calcRoundInfo(rs.StartTime) log.Debug("Scheduling next action", "height", rs.Height, "round", round, "step", rs.Step, "roundStartTime", roundStartTime, "elapsedRatio", elapsedRatio) switch rs.Step { case RoundStepNewHeight: // We should run RoundActionPropose when rs.StartTime passes. if elapsedRatio < 0 { // startTime is in the future. - time.Sleep(time.Duration((-1.0 * elapsedRatio) * float64(roundDuration))) + time.Sleep(time.Duration((-1.0 * elapsedRatio) * float64(RoundDuration))) } cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPropose} case RoundStepNewRound: @@ -333,15 +336,15 @@ func (cs *ConsensusState) stepTransitionRoutine() { cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPropose} case RoundStepPropose: // Wake up when it's time to vote. - time.Sleep(time.Duration((roundDeadlinePrevote - elapsedRatio) * float64(roundDuration))) + time.Sleep(time.Duration((roundDeadlinePrevote - elapsedRatio) * float64(RoundDuration))) cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrevote} case RoundStepPrevote: // Wake up when it's time to precommit. - time.Sleep(time.Duration((roundDeadlinePrecommit - elapsedRatio) * float64(roundDuration))) + time.Sleep(time.Duration((roundDeadlinePrecommit - elapsedRatio) * float64(RoundDuration))) cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrecommit} case RoundStepPrecommit: // Wake up when the round is over. - time.Sleep(time.Duration((1.0 - elapsedRatio) * float64(roundDuration))) + time.Sleep(time.Duration((1.0 - elapsedRatio) * float64(RoundDuration))) cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionTryCommit} case RoundStepCommit: // There's nothing to scheudle, we're waiting for @@ -1122,13 +1125,13 @@ func (cs *ConsensusState) SetEventSwitch(evsw *events.EventSwitch) { // total duration of given round func calcRoundDuration(round uint) time.Duration { - return roundDuration0 + roundDurationDelta*time.Duration(round) + return RoundDuration0 + RoundDurationDelta*time.Duration(round) } // startTime is when round zero started. func calcRoundStartTime(round uint, startTime time.Time) time.Time { - return startTime.Add(roundDuration0*time.Duration(round) + - roundDurationDelta*(time.Duration((int64(round)*int64(round)-int64(round))/2))) + return startTime.Add(RoundDuration0*time.Duration(round) + + RoundDurationDelta*(time.Duration((int64(round)*int64(round)-int64(round))/2))) } // calculates the current round given startTime of round zero. @@ -1142,8 +1145,8 @@ func calcRound(startTime time.Time) uint { // D_delta * R^2 + (2D_0 - D_delta) * R + 2(Start - Now) <= 0. // AR^2 + BR + C <= 0; A = D_delta, B = (2_D0 - D_delta), C = 2(Start - Now). // R = Floor((-B + Sqrt(B^2 - 4AC))/2A) - A := float64(roundDurationDelta) - B := 2.0*float64(roundDuration0) - float64(roundDurationDelta) + A := float64(RoundDurationDelta) + B := 2.0*float64(RoundDuration0) - float64(RoundDurationDelta) C := 2.0 * float64(startTime.Sub(now)) R := math.Floor((-B + math.Sqrt(B*B-4.0*A*C)) / (2 * A)) if math.IsNaN(R) { @@ -1160,12 +1163,12 @@ func calcRound(startTime time.Time) uint { // convenience // NOTE: elapsedRatio can be negative if startTime is in the future. -func calcRoundInfo(startTime time.Time) (round uint, roundStartTime time.Time, roundDuration time.Duration, +func calcRoundInfo(startTime time.Time) (round uint, roundStartTime time.Time, RoundDuration time.Duration, roundElapsed time.Duration, elapsedRatio float64) { round = calcRound(startTime) roundStartTime = calcRoundStartTime(round, startTime) - roundDuration = calcRoundDuration(round) + RoundDuration = calcRoundDuration(round) roundElapsed = time.Now().Sub(roundStartTime) - elapsedRatio = float64(roundElapsed) / float64(roundDuration) + elapsedRatio = float64(roundElapsed) / float64(RoundDuration) return } diff --git a/mempool/mempool.go b/mempool/mempool.go index 81919877..d5c6dad0 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -42,7 +42,7 @@ func (mem *Mempool) GetCache() *sm.BlockCache { func (mem *Mempool) AddTx(tx types.Tx) (err error) { mem.mtx.Lock() defer mem.mtx.Unlock() - err = sm.ExecTx(mem.cache, tx, false) + err = sm.ExecTx(mem.cache, tx, false, false) if err != nil { log.Debug("AddTx() error", "tx", tx, "error", err) return err @@ -93,7 +93,7 @@ func (mem *Mempool) ResetForBlockAndState(block *types.Block, state *sm.State) { // Next, filter all txs that aren't valid given new state. validTxs := []types.Tx{} for _, tx := range txs { - err := sm.ExecTx(mem.cache, tx, false) + err := sm.ExecTx(mem.cache, tx, false, false) if err == nil { log.Debug("Filter in, valid", "tx", tx) validTxs = append(validTxs, tx) diff --git a/rpc/test/client_rpc_test.go b/rpc/test/client_rpc_test.go index 0d712192..0d455c3d 100644 --- a/rpc/test/client_rpc_test.go +++ b/rpc/test/client_rpc_test.go @@ -1,10 +1,6 @@ package rpc import ( - "fmt" - "github.com/gorilla/websocket" - "github.com/tendermint/tendermint/rpc" - "net/http" "testing" ) @@ -77,34 +73,3 @@ func TestJSONCallCode(t *testing.T) { func TestJSONCallContract(t *testing.T) { testCall(t, "JSONRPC") } - -//-------------------------------------------------------------------------------- -// Test the websocket client - -func TestWSConnect(t *testing.T) { - dialer := websocket.DefaultDialer - rHeader := http.Header{} - _, r, err := dialer.Dial(websocketAddr, rHeader) - fmt.Println("respoinse:", r) - if err != nil { - t.Fatal(err) - } -} - -func TestWSSubscribe(t *testing.T) { - dialer := websocket.DefaultDialer - rHeader := http.Header{} - con, _, err := dialer.Dial(websocketAddr, rHeader) - if err != nil { - t.Fatal(err) - } - err = con.WriteJSON(rpc.WSRequest{ - Type: "subscribe", - Event: "newblock", - }) - if err != nil { - t.Fatal(err) - } - typ, p, err := con.ReadMessage() - fmt.Println("RESPONSE:", typ, string(p), err) -} diff --git a/rpc/test/client_ws_test.go b/rpc/test/client_ws_test.go new file mode 100644 index 00000000..435c10cf --- /dev/null +++ b/rpc/test/client_ws_test.go @@ -0,0 +1,327 @@ +package rpc + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/gorilla/websocket" + "github.com/tendermint/tendermint/binary" + "github.com/tendermint/tendermint/rpc" + "github.com/tendermint/tendermint/types" + "net/http" + "testing" + "time" +) + +//-------------------------------------------------------------------------------- +// Utilities for testing the websocket service + +// create a new connection +func newWSCon(t *testing.T) *websocket.Conn { + dialer := websocket.DefaultDialer + rHeader := http.Header{} + con, r, err := dialer.Dial(websocketAddr, rHeader) + fmt.Println("response", r) + if err != nil { + t.Fatal(err) + } + return con +} + +// subscribe to an event +func subscribe(t *testing.T, con *websocket.Conn, eventid string) { + err := con.WriteJSON(rpc.WSRequest{ + Type: "subscribe", + Event: eventid, + }) + if err != nil { + t.Fatal(err) + } +} + +// unsubscribe from an event +func unsubscribe(t *testing.T, con *websocket.Conn, eventid string) { + err := con.WriteJSON(rpc.WSRequest{ + Type: "unsubscribe", + Event: eventid, + }) + if err != nil { + t.Fatal(err) + } +} + +// wait for an event, do things that might trigger events, and check them when they are received +func waitForEvent(t *testing.T, con *websocket.Conn, eventid string, dieOnTimeout bool, f func(), check func(string, []byte) error) { + // go routine to wait for webscoket msg + gch := make(chan []byte) + ech := make(chan error) + go func() { + typ, p, err := con.ReadMessage() + fmt.Println("RESPONSE:", typ, string(p), err) + if err != nil { + ech <- err + } else { + gch <- p + } + }() + + // do stuff (transactions) + f() + + // if the event is not received in 20 seconds, die + ticker := time.Tick(10 * time.Second) + select { + case <-ticker: + if dieOnTimeout { + con.Close() + t.Fatalf("%s event was not received in time", eventid) + } + // else that's great, we didn't hear the event + case p := <-gch: + if dieOnTimeout { + // message was received and expected + // run the check + err := check(eventid, p) + if err != nil { + t.Fatal(err) + } + } else { + con.Close() + t.Fatalf("%s event was not expected", eventid) + } + case err := <-ech: + t.Fatal(err) + } +} + +func unmarshalResponseNewBlock(b []byte) (*types.Block, error) { + // unmarshall and assert somethings + var response struct { + Event string + Data *types.Block + Error string + } + var err error + binary.ReadJSON(&response, b, &err) + if err != nil { + return nil, err + } + if response.Error != "" { + return nil, fmt.Errorf(response.Error) + } + block := response.Data + return block, nil +} + +//-------------------------------------------------------------------------------- +// Test the websocket service + +// make a simple connection to the server +func TestWSConnect(t *testing.T) { + con := newWSCon(t) + con.Close() +} + +// receive a new block message +func _TestWSNewBlock(t *testing.T) { + con := newWSCon(t) + eid := types.EventStringNewBlock() + subscribe(t, con, eid) + defer func() { + unsubscribe(t, con, eid) + con.Close() + }() + waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error { + fmt.Println("Check:", string(b)) + return nil + }) +} + +// receive a few new block messages in a row, with increasing height +func TestWSBlockchainGrowth(t *testing.T) { + con := newWSCon(t) + eid := types.EventStringNewBlock() + subscribe(t, con, eid) + defer func() { + unsubscribe(t, con, eid) + con.Close() + }() + var initBlockN uint + for i := 0; i < 2; i++ { + waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error { + block, err := unmarshalResponseNewBlock(b) + if err != nil { + return err + } + if i == 0 { + initBlockN = block.Header.Height + } else { + if block.Header.Height != initBlockN+uint(i) { + return fmt.Errorf("Expected block %d, got block %d", i, block.Header.Height) + } + } + + return nil + }) + } +} + +// send a transaction and validate the events from listening for both sender and receiver +func TestWSSend(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} + amt := uint64(100) + + con := newWSCon(t) + eidInput := types.EventStringAccInput(byteAddr) + eidOutput := types.EventStringAccOutput(toAddr) + subscribe(t, con, eidInput) + subscribe(t, con, eidOutput) + defer func() { + unsubscribe(t, con, eidInput) + unsubscribe(t, con, eidOutput) + con.Close() + }() + checkerFunc := func(eid string, b []byte) error { + // unmarshal and assert correctness + var response struct { + Event string + Data types.SendTx + Error string + } + var err error + binary.ReadJSON(&response, b, &err) + if err != nil { + return err + } + if response.Error != "" { + return fmt.Errorf(response.Error) + } + if eid != response.Event { + 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 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 nil + } + waitForEvent(t, con, eidInput, true, func() { + broadcastTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) + }, checkerFunc) + waitForEvent(t, con, eidOutput, true, func() {}, checkerFunc) +} + +// ensure events are only fired once for a given transaction +func TestWSDoubleFire(t *testing.T) { + con := newWSCon(t) + eid := types.EventStringAccInput(byteAddr) + subscribe(t, con, eid) + defer func() { + unsubscribe(t, con, eid) + con.Close() + }() + 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} + // broadcast the transaction, wait to hear about it + waitForEvent(t, con, eid, true, func() { + broadcastTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) + }, func(eid string, b []byte) error { + return nil + }) + // but make sure we don't hear about it twice + waitForEvent(t, con, eid, false, func() { + }, func(eid string, b []byte) error { + return nil + }) +} + +// create a contract and send it a msg, validate the return +func TestWSCall(t *testing.T) { + byteAddr, _ := hex.DecodeString(userAddr) + con := newWSCon(t) + eid := types.EventStringAccInput(byteAddr) + subscribe(t, con, eid) + defer func() { + unsubscribe(t, con, eid) + con.Close() + }() + amt := uint64(10000) + code, returnCode, returnVal := simpleCallContract() + var contractAddr []byte + // wait for the contract to be created + waitForEvent(t, con, eid, true, func() { + _, receipt := broadcastTx(t, "JSONRPC", byteAddr, nil, code, byteKey, amt, 1000, 1000) + contractAddr = receipt.ContractAddr + + }, func(eid string, b []byte) error { + // unmarshall and assert somethings + var response struct { + Event string + Data struct { + Tx types.CallTx + Return []byte + Exception string + } + Error string + } + var err error + binary.ReadJSON(&response, b, &err) + if err != nil { + return err + } + if response.Error != "" { + return fmt.Errorf(response.Error) + } + if response.Data.Exception != "" { + 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 tx.Input.Amount != amt { + return fmt.Errorf("Amt does not match up! Got %d, expected %d", tx.Input.Amount, amt) + } + ret := response.Data.Return + if bytes.Compare(ret, returnCode) != 0 { + return fmt.Errorf("Create did not return correct byte code for new contract. Got %x, expected %x", ret, returnCode) + } + return nil + }) + + // 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) + }, func(eid string, b []byte) error { + // unmarshall and assert somethings + var response struct { + Event string + Data struct { + Tx types.CallTx + Return []byte + Exception string + } + Error string + } + var err error + binary.ReadJSON(&response, b, &err) + if err != nil { + return err + } + if response.Error != "" { + return fmt.Errorf(response.Error) + } + ret := response.Data.Return + if bytes.Compare(ret, returnVal) != 0 { + return fmt.Errorf("Call did not return correctly. Got %x, expected %x", ret, returnVal) + } + return nil + }) +} diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 82650c18..192c132b 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -6,6 +6,7 @@ import ( "github.com/tendermint/tendermint/account" . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/logger" nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" @@ -14,6 +15,7 @@ import ( "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" "testing" + "time" ) // global variables for use across all tests @@ -26,9 +28,10 @@ var ( mempoolCount = 0 - userAddr = "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB" - userPriv = "FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" - userPub = "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" + userAddr = "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB" + userPriv = "FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" + userPub = "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" + byteAddr, byteKey = initUserBytes() clients = map[string]cclient.Client{ "JSONRPC": cclient.NewClient(requestAddr, "JSONRPC"), @@ -36,6 +39,14 @@ var ( } ) +func initUserBytes() ([]byte, [64]byte) { + byteAddr, _ := hex.DecodeString(userAddr) + var byteKey [64]byte + oh, _ := hex.DecodeString(userPriv) + copy(byteKey[:], oh) + return byteAddr, byteKey +} + func decodeHex(hexStr string) []byte { bytes, err := hex.DecodeString(hexStr) if err != nil { @@ -84,6 +95,9 @@ func init() { priv.SetFile(rootDir + "/priv_validator.json") priv.Save() + consensus.RoundDuration0 = 3 * time.Second + consensus.RoundDurationDelta = 1 * time.Second + // start a node ready := make(chan struct{}) go newNode(ready) @@ -264,3 +278,20 @@ func checkTx(t *testing.T, fromAddr []byte, priv *account.PrivAccount, tx *types t.Fatal(types.ErrTxInvalidSignature) } } + +// simple contract returns 5 + 6 = 0xb +func simpleCallContract() ([]byte, []byte, []byte) { + // this is the code we want to run when the contract is called + contractCode := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} + // the is the code we need to return the contractCode when the contract is initialized + lenCode := len(contractCode) + // push code to the stack + //code := append([]byte{byte(0x60 + lenCode - 1)}, LeftPadWord256(contractCode).Bytes()...) + code := append([]byte{0x7f}, RightPadWord256(contractCode).Bytes()...) + // store it in memory + code = append(code, []byte{0x60, 0x0, 0x52}...) + // return whats in memory + //code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...) + code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...) + return code, contractCode, LeftPadBytes([]byte{0xb}, 32) +} diff --git a/rpc/test/tests.go b/rpc/test/tests.go index 8fbd81e3..4d8abb84 100644 --- a/rpc/test/tests.go +++ b/rpc/test/tests.go @@ -49,11 +49,6 @@ func testGetAccount(t *testing.T, typ string) { } func testSignedTx(t *testing.T, typ string) { - byteAddr, _ := hex.DecodeString(userAddr) - var byteKey [64]byte - oh, _ := hex.DecodeString(userPriv) - copy(byteKey[:], oh) - 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) @@ -69,11 +64,6 @@ func testSignedTx(t *testing.T, typ string) { } func testBroadcastTx(t *testing.T, typ string) { - byteAddr, _ := hex.DecodeString(userAddr) - var byteKey [64]byte - oh, _ := hex.DecodeString(userPriv) - copy(byteKey[:], oh) - 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) @@ -103,11 +93,6 @@ func testGetStorage(t *testing.T, typ string) { _ = priv //core.SetPrivValidator(priv) - byteAddr, _ := hex.DecodeString(userAddr) - var byteKey [64]byte - oh, _ := hex.DecodeString(userPriv) - copy(byteKey[:], oh) - amt := uint64(1100) code := []byte{0x60, 0x5, 0x60, 0x1, 0x55} _, receipt := broadcastTx(t, typ, byteAddr, nil, code, byteKey, amt, 1000, 1000) @@ -153,30 +138,9 @@ func testCallCode(t *testing.T, typ string) { func testCall(t *testing.T, typ string) { client := clients[typ] - priv := state.LoadPrivValidator(".tendermint/priv_validator.json") - _ = priv - //core.SetPrivValidator(priv) - - byteAddr, _ := hex.DecodeString(userAddr) - var byteKey [64]byte - oh, _ := hex.DecodeString(userPriv) - copy(byteKey[:], oh) - // create the contract amt := uint64(6969) - // this is the code we want to run when the contract is called - contractCode := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} - // the is the code we need to return the contractCode when the contract is initialized - lenCode := len(contractCode) - // push code to the stack - //code := append([]byte{byte(0x60 + lenCode - 1)}, LeftPadWord256(contractCode).Bytes()...) - code := append([]byte{0x7f}, RightPadWord256(contractCode).Bytes()...) - // store it in memory - code = append(code, []byte{0x60, 0x0, 0x52}...) - // return whats in memory - //code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...) - code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...) - + code, _, _ := simpleCallContract() _, receipt := broadcastTx(t, typ, byteAddr, nil, code, byteKey, amt, 1000, 1000) if receipt.CreatesContract == 0 { t.Fatal("This tx creates a contract") diff --git a/state/execution.go b/state/execution.go index dc0cb8c3..0d687768 100644 --- a/state/execution.go +++ b/state/execution.go @@ -13,7 +13,7 @@ import ( // NOTE: If an error occurs during block execution, state will be left // at an invalid state. Copy the state before calling ExecBlock! func ExecBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader) error { - err := execBlock(s, block, blockPartsHeader) + err := execBlock(s, block, blockPartsHeader, true) if err != nil { return err } @@ -29,7 +29,7 @@ func ExecBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade // executes transactions of a block, does not check block.StateHash // NOTE: If an error occurs during block execution, state will be left // at an invalid state. Copy the state before calling execBlock! -func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader) error { +func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader, fireEvents bool) error { // Basic block validation. err := block.ValidateBasic(s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime) if err != nil { @@ -111,7 +111,7 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade // Commit each tx for _, tx := range block.Data.Txs { - err := ExecTx(blockCache, tx, true) + err := ExecTx(blockCache, tx, true, fireEvents) if err != nil { return InvalidTxError{tx, err} } @@ -291,7 +291,7 @@ func adjustByOutputs(accounts map[string]*account.Account, outs []*types.TxOutpu // If the tx is invalid, an error will be returned. // Unlike ExecBlock(), state will not be altered. -func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool) error { +func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall, fireEvents bool) error { // TODO: do something with fees fees := uint64(0) @@ -329,7 +329,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool) error { // If we're in a block (not mempool), // fire event on all inputs and outputs // see types/events.go for spec - if runCall { + if fireEvents { for _, i := range tx.Inputs { _s.evsw.FireEvent(types.EventStringAccInput(i.Address), tx) } @@ -449,19 +449,21 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool) error { // Create a receipt from the ret and whether errored. log.Info("VM call complete", "caller", caller, "callee", callee, "return", ret, "err", err) - // 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}) + 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.EventStringAccReceive(tx.Address), struct { + Tx types.Tx + Return []byte + Exception string + }{tx, ret, exception}) + } } else { // The mempool does not call txs until // the proposer determines the order of txs. @@ -529,7 +531,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool) error { if !added { panic("Failed to add validator") } - if runCall { + if fireEvents { _s.evsw.FireEvent(types.EventStringBond(), tx) } return nil @@ -554,7 +556,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool) error { // Good! _s.unbondValidator(val) - if runCall { + if fireEvents { _s.evsw.FireEvent(types.EventStringUnbond(), tx) } return nil @@ -579,7 +581,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool) error { // Good! _s.rebondValidator(val) - if runCall { + if fireEvents { _s.evsw.FireEvent(types.EventStringRebond(), tx) } return nil @@ -625,7 +627,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool) error { // Good! (Bad validator!) _s.destroyValidator(accused) - if runCall { + if fireEvents { _s.evsw.FireEvent(types.EventStringDupeout(), tx) } return nil diff --git a/state/state.go b/state/state.go index c8d7037c..4bd3da42 100644 --- a/state/state.go +++ b/state/state.go @@ -119,7 +119,7 @@ func (s *State) Hash() []byte { // Mutates the block in place and updates it with new state hash. func (s *State) SetBlockStateHash(block *types.Block) error { sCopy := s.Copy() - err := execBlock(sCopy, block, types.PartSetHeader{}) + err := execBlock(sCopy, block, types.PartSetHeader{}, false) // don't fire events if err != nil { return err } diff --git a/vm/vm.go b/vm/vm.go index cbf68729..b37d9ca3 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -6,6 +6,7 @@ import ( "math/big" . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/events" "github.com/tendermint/tendermint/vm/sha3" ) @@ -45,6 +46,8 @@ type VM struct { origin Word256 callDepth int + + evsw *events.EventSwitch } func NewVM(appState AppState, params Params, origin Word256) *VM { @@ -56,6 +59,11 @@ func NewVM(appState AppState, params Params, origin Word256) *VM { } } +// satisfies events.Eventable +func (vm *VM) SetEventSwitch(evsw *events.EventSwitch) { + vm.evsw = evsw +} + // CONTRACT appState is aware of caller and callee, so we can just mutate them. // value: To be transferred from caller to callee. Refunded upon error. // gas: Available gas. No refunds for gas.