diff --git a/binary/codec.go b/binary/codec.go index 954a50cf..79799d9f 100644 --- a/binary/codec.go +++ b/binary/codec.go @@ -119,7 +119,7 @@ func BasicCodecDecoder(r Unreader, n *int64, err *error) (o interface{}) { o = ReadTime(r, n, err) default: if *err != nil { - panic(err) + panic(*err) } else { panic(fmt.Sprintf("Unsupported type byte: %X", type_)) } diff --git a/binary/log.go b/binary/log.go index 7dc9b319..28586125 100644 --- a/binary/log.go +++ b/binary/log.go @@ -11,6 +11,7 @@ func init() { log.SetHandler( log15.LvlFilterHandler( log15.LvlWarn, + //log15.LvlDebug, logger.RootHandler(), ), ) diff --git a/block/tx.go b/block/tx.go index 2a5ca034..3a841a39 100644 --- a/block/tx.go +++ b/block/tx.go @@ -15,7 +15,6 @@ var ( ErrTxInsufficientFunds = errors.New("Error insufficient funds") ErrTxUnknownPubKey = errors.New("Error unknown pubkey") ErrTxInvalidPubKey = errors.New("Error invalid pubkey") - ErrTxRedeclaredPubKey = errors.New("Error redeclared pubkey") ErrTxInvalidSignature = errors.New("Error invalid signature") ErrTxInvalidSequence = errors.New("Error invalid sequence") ) diff --git a/consensus/state.go b/consensus/state.go index c26eca06..ac670001 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -646,6 +646,7 @@ func (cs *ConsensusState) RunActionPropose(height uint, round uint) { err := cs.PrivValidator.SignProposal(proposal) if err == nil { log.Info("Signed and set proposal", "height", cs.Height, "round", cs.Round, "proposal", proposal) + log.Debug(Fmt("Signed and set proposal block: %v", block)) // Set fields cs.Proposal = proposal cs.ProposalBlock = block diff --git a/db/db.go b/db/db.go index 241a0f42..afdea3da 100644 --- a/db/db.go +++ b/db/db.go @@ -13,6 +13,9 @@ type DB interface { SetSync([]byte, []byte) Delete([]byte) DeleteSync([]byte) + + // For debugging + Print() } //----------------------------------------------------------------------------- diff --git a/mempool/mempool.go b/mempool/mempool.go index 3c7abb4d..37dc8233 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -34,8 +34,10 @@ func (mem *Mempool) AddTx(tx Tx) (err error) { defer mem.mtx.Unlock() err = mem.state.ExecTx(tx) if err != nil { + log.Debug("AddTx() error", "tx", tx, "error", err) return err } else { + log.Debug("AddTx() success", "tx", tx) mem.txs = append(mem.txs, tx) return nil } @@ -44,6 +46,7 @@ func (mem *Mempool) AddTx(tx Tx) (err error) { func (mem *Mempool) GetProposalTxs() []Tx { mem.mtx.Lock() defer mem.mtx.Unlock() + log.Debug("GetProposalTxs:", "txs", mem.txs) return mem.txs } @@ -68,8 +71,10 @@ func (mem *Mempool) ResetForBlockAndState(block *Block, state *state.State) { for _, tx := range mem.txs { txHash := BinarySha256(tx) if _, ok := blockTxsMap[string(txHash)]; ok { + log.Debug("Filter out, already committed", "tx", tx, "txHash", txHash) continue } else { + log.Debug("Filter in, still new", "tx", tx, "txHash", txHash) txs = append(txs, tx) } } @@ -78,13 +83,16 @@ func (mem *Mempool) ResetForBlockAndState(block *Block, state *state.State) { validTxs := []Tx{} for _, tx := range txs { err := mem.state.ExecTx(tx) - if err != nil { + if err == nil { + log.Debug("Filter in, valid", "tx", tx) validTxs = append(validTxs, tx) } else { // tx is no longer valid. + log.Debug("Filter out, no longer valid", "tx", tx, "error", err) } } // We're done! + log.Debug("New txs", "txs", validTxs, "oldTxs", mem.txs) mem.txs = validTxs } diff --git a/merkle/iavl_node.go b/merkle/iavl_node.go index 3a1ea689..8ffc719d 100644 --- a/merkle/iavl_node.go +++ b/merkle/iavl_node.go @@ -67,7 +67,7 @@ func (node *IAVLNode) _copy() *IAVLNode { leftNode: node.leftNode, rightHash: node.rightHash, rightNode: node.rightNode, - persisted: node.persisted, + persisted: false, // Going to be mutated, so it can't already be persisted. } } diff --git a/merkle/iavl_tree.go b/merkle/iavl_tree.go index f9c19ae4..d4203112 100644 --- a/merkle/iavl_tree.go +++ b/merkle/iavl_tree.go @@ -6,6 +6,7 @@ import ( "sync" . "github.com/tendermint/tendermint/binary" + . "github.com/tendermint/tendermint/common" db_ "github.com/tendermint/tendermint/db" ) @@ -205,12 +206,16 @@ func (ndb *nodeDB) GetNode(t *IAVLTree, hash []byte) *IAVLNode { } else { // Doesn't exist, load. buf := ndb.db.Get(hash) + if len(buf) == 0 { + ndb.db.(*db_.LevelDB).Print() + panic(Fmt("Value missing for key %X", hash)) + } r := bytes.NewReader(buf) var n int64 var err error node := ReadIAVLNode(t, r, &n, &err) if err != nil { - panic(err) + panic(Fmt("Error reading IAVLNode. bytes: %X error: %v", buf, err)) } node.persisted = true ndb.cacheNode(node) diff --git a/rpc/account.go b/rpc/account.go new file mode 100644 index 00000000..4c07bf3b --- /dev/null +++ b/rpc/account.go @@ -0,0 +1,56 @@ +package rpc + +import ( + "net/http" + + "github.com/tendermint/tendermint/account" + "github.com/tendermint/tendermint/binary" + "github.com/tendermint/tendermint/block" + . "github.com/tendermint/tendermint/common" +) + +type GenPrivAccountResponse struct { + PrivAccount *account.PrivAccount +} + +func GenPrivAccountHandler(w http.ResponseWriter, r *http.Request) { + privAccount := account.GenPrivAccount() + + res := GenPrivAccountResponse{ + PrivAccount: privAccount, + } + WriteAPIResponse(w, API_OK, res) +} + +//----------------------------------------------------------------------------- + +type SignSendTxResponse struct { + SendTx *block.SendTx +} + +func SignSendTxHandler(w http.ResponseWriter, r *http.Request) { + sendTxStr := GetParam(r, "sendTx") + privAccountsStr := GetParam(r, "privAccounts") + + var err error + sendTx := binary.ReadJSON(&block.SendTx{}, []byte(sendTxStr), &err).(*block.SendTx) + if err != nil { + WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid sendTx: %v", err)) + return + } + privAccounts := binary.ReadJSON([]*account.PrivAccount{}, []byte(privAccountsStr), &err).([]*account.PrivAccount) + if err != nil { + WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid privAccounts: %v", err)) + return + } + + for i, input := range sendTx.Inputs { + input.PubKey = privAccounts[i].PubKey + input.Signature = privAccounts[i].Sign(sendTx) + } + + res := SignSendTxResponse{ + SendTx: sendTx, + } + WriteAPIResponse(w, API_OK, res) +} diff --git a/rpc/blocks.go b/rpc/blocks.go index c6d5e4c2..57be068e 100644 --- a/rpc/blocks.go +++ b/rpc/blocks.go @@ -21,11 +21,12 @@ func BlockchainInfoHandler(w http.ResponseWriter, r *http.Request) { maxHeight = MinUint(blockStore.Height(), maxHeight) } if minHeight == 0 { - minHeight = MaxUint(1, MinUint(maxHeight-20, 1)) + minHeight = uint(MaxInt(1, int(maxHeight)-20)) } + log.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) blockMetas := []*BlockMeta{} - for height := minHeight; height <= maxHeight; height++ { + for height := maxHeight; height >= minHeight; height-- { blockMeta := blockStore.LoadBlockMeta(height) blockMetas = append(blockMetas, blockMeta) } @@ -41,6 +42,11 @@ func BlockchainInfoHandler(w http.ResponseWriter, r *http.Request) { //----------------------------------------------------------------------------- +type BlockResponse struct { + BlockMeta *BlockMeta + Block *Block +} + func BlockHandler(w http.ResponseWriter, r *http.Request) { height, _ := GetParamUint(r, "height") if height == 0 { @@ -52,7 +58,12 @@ func BlockHandler(w http.ResponseWriter, r *http.Request) { return } + blockMeta := blockStore.LoadBlockMeta(height) block := blockStore.LoadBlock(height) - WriteAPIResponse(w, API_OK, block) + res := BlockResponse{ + BlockMeta: blockMeta, + Block: block, + } + WriteAPIResponse(w, API_OK, res) return } diff --git a/rpc/http_handler.go b/rpc/http_handler.go index 0f9ef39e..64529de9 100644 --- a/rpc/http_handler.go +++ b/rpc/http_handler.go @@ -43,6 +43,8 @@ func WriteAPIResponse(w http.ResponseWriter, status APIStatus, data interface{}) } w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + /* Bad idea: (e.g. hard to use with jQuery) switch res.Status { case API_OK: w.WriteHeader(200) @@ -56,7 +58,7 @@ func WriteAPIResponse(w http.ResponseWriter, status APIStatus, data interface{}) w.WriteHeader(430) default: w.WriteHeader(440) - } + }*/ w.Write(buf.Bytes()) } diff --git a/rpc/http_server.go b/rpc/http_server.go index bd8d3b75..774f0c5e 100644 --- a/rpc/http_server.go +++ b/rpc/http_server.go @@ -12,6 +12,8 @@ func StartHTTPServer() { http.HandleFunc("/blockchain", BlockchainInfoHandler) http.HandleFunc("/block", BlockHandler) http.HandleFunc("/broadcast_tx", BroadcastTxHandler) + http.HandleFunc("/gen_priv_account", GenPrivAccountHandler) + http.HandleFunc("/sign_send_tx", SignSendTxHandler) log.Info(Fmt("Starting RPC HTTP server on %s", config.App.GetString("RPC.HTTP.ListenAddr"))) diff --git a/rpc/mempool.go b/rpc/mempool.go index dafe7386..6bd795c3 100644 --- a/rpc/mempool.go +++ b/rpc/mempool.go @@ -11,7 +11,8 @@ import ( func BroadcastTxHandler(w http.ResponseWriter, r *http.Request) { txJSON := GetParam(r, "tx") var err error - tx := ReadJSON(struct{ Tx }{}, []byte(txJSON), &err).(struct{ Tx }).Tx + var tx Tx + ReadJSON(&tx, []byte(txJSON), &err) if err != nil { WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid tx: %v", err)) return diff --git a/state/state.go b/state/state.go index 7541ef2c..e03bdcc6 100644 --- a/state/state.go +++ b/state/state.go @@ -110,7 +110,8 @@ func (s *State) Copy() *State { // The accounts from the TxInputs must either already have // account.PubKey.(type) != PubKeyNil, (it must be known), -// or it must be specified in the TxInput. But not both. +// or it must be specified in the TxInput. If redeclared, +// the TxInput is modified and input.PubKey set to PubKeyNil. func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string]*Account, error) { accounts := map[string]*Account{} for _, in := range ins { @@ -132,9 +133,7 @@ func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string] } account.PubKey = in.PubKey } else { - if _, isNil := in.PubKey.(PubKeyNil); !isNil { - return nil, ErrTxRedeclaredPubKey - } + in.PubKey = PubKeyNil{} } accounts[string(in.Address)] = account } diff --git a/state/state_test.go b/state/state_test.go index 5e13f82d..c2d81814 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -143,6 +143,75 @@ func TestGenesisSaveLoad(t *testing.T) { } } +/* TODO: Write better tests, and also refactor out common code +func TestSendTxStateSave(t *testing.T) { + + // Generate a state, save & load it. + s0, privAccounts, _ := RandGenesisState(10, true, 1000, 5, true, 1000) + + sendTx := &SendTx{ + Inputs: []*TxInput{ + &TxInput{ + Address: privAccounts[0].PubKey.Address(), + Amount: 100, + Sequence: 1, + Signature: nil, + PubKey: privAccounts[0].PubKey, + }, + }, + Outputs: []*TxOutput{ + &TxOutput{ + Address: []byte("01234567890123456789"), + Amount: 100, + }, + }, + } + sendTx.Inputs[0].Signature = privAccounts[0].Sign(sendTx) + + // Mutate the state to append block with sendTx + block := &Block{ + Header: &Header{ + Network: config.App.GetString("Network"), + Height: 1, + Time: s0.LastBlockTime.Add(time.Minute), + Fees: 0, + NumTxs: 1, + LastBlockHash: s0.LastBlockHash, + LastBlockParts: s0.LastBlockParts, + StateHash: nil, + }, + Validation: &Validation{}, + Data: &Data{ + Txs: []Tx{sendTx}, + }, + } + blockParts := NewPartSetFromData(BinaryBytes(block)) + + // The last argument to AppendBlock() is `false`, + // which sets Block.Header.StateHash. + err := s0.Copy().AppendBlock(block, blockParts.Header(), false) + if err != nil { + t.Error("Error appending initial block:", err) + } + if len(block.Header.StateHash) == 0 { + t.Error("Expected StateHash but got nothing.") + } + + // Now append the block to s0. + // This time we also check the StateHash (as computed above). + err = s0.AppendBlock(block, blockParts.Header(), true) + if err != nil { + t.Error("Error appending initial block:", err) + } + + // Save s0 + s0.Save() + fmt.Printf("s0.accounts.Hash(): %X\n", s0.accounts.Hash()) + s0.DB.Print() + +} +*/ + func TestTxSequence(t *testing.T) { state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)