rpc/account; fixed mempool tx filter bug; fixed iavl_tree persistence
bug
This commit is contained in:
parent
4a81b06b6e
commit
fe566739fd
|
@ -119,7 +119,7 @@ func BasicCodecDecoder(r Unreader, n *int64, err *error) (o interface{}) {
|
||||||
o = ReadTime(r, n, err)
|
o = ReadTime(r, n, err)
|
||||||
default:
|
default:
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
panic(err)
|
panic(*err)
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintf("Unsupported type byte: %X", type_))
|
panic(fmt.Sprintf("Unsupported type byte: %X", type_))
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ func init() {
|
||||||
log.SetHandler(
|
log.SetHandler(
|
||||||
log15.LvlFilterHandler(
|
log15.LvlFilterHandler(
|
||||||
log15.LvlWarn,
|
log15.LvlWarn,
|
||||||
|
//log15.LvlDebug,
|
||||||
logger.RootHandler(),
|
logger.RootHandler(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,7 +15,6 @@ var (
|
||||||
ErrTxInsufficientFunds = errors.New("Error insufficient funds")
|
ErrTxInsufficientFunds = errors.New("Error insufficient funds")
|
||||||
ErrTxUnknownPubKey = errors.New("Error unknown pubkey")
|
ErrTxUnknownPubKey = errors.New("Error unknown pubkey")
|
||||||
ErrTxInvalidPubKey = errors.New("Error invalid pubkey")
|
ErrTxInvalidPubKey = errors.New("Error invalid pubkey")
|
||||||
ErrTxRedeclaredPubKey = errors.New("Error redeclared pubkey")
|
|
||||||
ErrTxInvalidSignature = errors.New("Error invalid signature")
|
ErrTxInvalidSignature = errors.New("Error invalid signature")
|
||||||
ErrTxInvalidSequence = errors.New("Error invalid sequence")
|
ErrTxInvalidSequence = errors.New("Error invalid sequence")
|
||||||
)
|
)
|
||||||
|
|
|
@ -646,6 +646,7 @@ func (cs *ConsensusState) RunActionPropose(height uint, round uint) {
|
||||||
err := cs.PrivValidator.SignProposal(proposal)
|
err := cs.PrivValidator.SignProposal(proposal)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Info("Signed and set proposal", "height", cs.Height, "round", cs.Round, "proposal", proposal)
|
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
|
// Set fields
|
||||||
cs.Proposal = proposal
|
cs.Proposal = proposal
|
||||||
cs.ProposalBlock = block
|
cs.ProposalBlock = block
|
||||||
|
|
3
db/db.go
3
db/db.go
|
@ -13,6 +13,9 @@ type DB interface {
|
||||||
SetSync([]byte, []byte)
|
SetSync([]byte, []byte)
|
||||||
Delete([]byte)
|
Delete([]byte)
|
||||||
DeleteSync([]byte)
|
DeleteSync([]byte)
|
||||||
|
|
||||||
|
// For debugging
|
||||||
|
Print()
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -34,8 +34,10 @@ func (mem *Mempool) AddTx(tx Tx) (err error) {
|
||||||
defer mem.mtx.Unlock()
|
defer mem.mtx.Unlock()
|
||||||
err = mem.state.ExecTx(tx)
|
err = mem.state.ExecTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debug("AddTx() error", "tx", tx, "error", err)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
log.Debug("AddTx() success", "tx", tx)
|
||||||
mem.txs = append(mem.txs, tx)
|
mem.txs = append(mem.txs, tx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -44,6 +46,7 @@ func (mem *Mempool) AddTx(tx Tx) (err error) {
|
||||||
func (mem *Mempool) GetProposalTxs() []Tx {
|
func (mem *Mempool) GetProposalTxs() []Tx {
|
||||||
mem.mtx.Lock()
|
mem.mtx.Lock()
|
||||||
defer mem.mtx.Unlock()
|
defer mem.mtx.Unlock()
|
||||||
|
log.Debug("GetProposalTxs:", "txs", mem.txs)
|
||||||
return mem.txs
|
return mem.txs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +71,10 @@ func (mem *Mempool) ResetForBlockAndState(block *Block, state *state.State) {
|
||||||
for _, tx := range mem.txs {
|
for _, tx := range mem.txs {
|
||||||
txHash := BinarySha256(tx)
|
txHash := BinarySha256(tx)
|
||||||
if _, ok := blockTxsMap[string(txHash)]; ok {
|
if _, ok := blockTxsMap[string(txHash)]; ok {
|
||||||
|
log.Debug("Filter out, already committed", "tx", tx, "txHash", txHash)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
log.Debug("Filter in, still new", "tx", tx, "txHash", txHash)
|
||||||
txs = append(txs, tx)
|
txs = append(txs, tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,13 +83,16 @@ func (mem *Mempool) ResetForBlockAndState(block *Block, state *state.State) {
|
||||||
validTxs := []Tx{}
|
validTxs := []Tx{}
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
err := mem.state.ExecTx(tx)
|
err := mem.state.ExecTx(tx)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
|
log.Debug("Filter in, valid", "tx", tx)
|
||||||
validTxs = append(validTxs, tx)
|
validTxs = append(validTxs, tx)
|
||||||
} else {
|
} else {
|
||||||
// tx is no longer valid.
|
// tx is no longer valid.
|
||||||
|
log.Debug("Filter out, no longer valid", "tx", tx, "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're done!
|
// We're done!
|
||||||
|
log.Debug("New txs", "txs", validTxs, "oldTxs", mem.txs)
|
||||||
mem.txs = validTxs
|
mem.txs = validTxs
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (node *IAVLNode) _copy() *IAVLNode {
|
||||||
leftNode: node.leftNode,
|
leftNode: node.leftNode,
|
||||||
rightHash: node.rightHash,
|
rightHash: node.rightHash,
|
||||||
rightNode: node.rightNode,
|
rightNode: node.rightNode,
|
||||||
persisted: node.persisted,
|
persisted: false, // Going to be mutated, so it can't already be persisted.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
. "github.com/tendermint/tendermint/binary"
|
. "github.com/tendermint/tendermint/binary"
|
||||||
|
. "github.com/tendermint/tendermint/common"
|
||||||
db_ "github.com/tendermint/tendermint/db"
|
db_ "github.com/tendermint/tendermint/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -205,12 +206,16 @@ func (ndb *nodeDB) GetNode(t *IAVLTree, hash []byte) *IAVLNode {
|
||||||
} else {
|
} else {
|
||||||
// Doesn't exist, load.
|
// Doesn't exist, load.
|
||||||
buf := ndb.db.Get(hash)
|
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)
|
r := bytes.NewReader(buf)
|
||||||
var n int64
|
var n int64
|
||||||
var err error
|
var err error
|
||||||
node := ReadIAVLNode(t, r, &n, &err)
|
node := ReadIAVLNode(t, r, &n, &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(Fmt("Error reading IAVLNode. bytes: %X error: %v", buf, err))
|
||||||
}
|
}
|
||||||
node.persisted = true
|
node.persisted = true
|
||||||
ndb.cacheNode(node)
|
ndb.cacheNode(node)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -21,11 +21,12 @@ func BlockchainInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
maxHeight = MinUint(blockStore.Height(), maxHeight)
|
maxHeight = MinUint(blockStore.Height(), maxHeight)
|
||||||
}
|
}
|
||||||
if minHeight == 0 {
|
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{}
|
blockMetas := []*BlockMeta{}
|
||||||
for height := minHeight; height <= maxHeight; height++ {
|
for height := maxHeight; height >= minHeight; height-- {
|
||||||
blockMeta := blockStore.LoadBlockMeta(height)
|
blockMeta := blockStore.LoadBlockMeta(height)
|
||||||
blockMetas = append(blockMetas, blockMeta)
|
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) {
|
func BlockHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
height, _ := GetParamUint(r, "height")
|
height, _ := GetParamUint(r, "height")
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
|
@ -52,7 +58,12 @@ func BlockHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blockMeta := blockStore.LoadBlockMeta(height)
|
||||||
block := blockStore.LoadBlock(height)
|
block := blockStore.LoadBlock(height)
|
||||||
WriteAPIResponse(w, API_OK, block)
|
res := BlockResponse{
|
||||||
|
BlockMeta: blockMeta,
|
||||||
|
Block: block,
|
||||||
|
}
|
||||||
|
WriteAPIResponse(w, API_OK, res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@ func WriteAPIResponse(w http.ResponseWriter, status APIStatus, data interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(200)
|
||||||
|
/* Bad idea: (e.g. hard to use with jQuery)
|
||||||
switch res.Status {
|
switch res.Status {
|
||||||
case API_OK:
|
case API_OK:
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
|
@ -56,7 +58,7 @@ func WriteAPIResponse(w http.ResponseWriter, status APIStatus, data interface{})
|
||||||
w.WriteHeader(430)
|
w.WriteHeader(430)
|
||||||
default:
|
default:
|
||||||
w.WriteHeader(440)
|
w.WriteHeader(440)
|
||||||
}
|
}*/
|
||||||
w.Write(buf.Bytes())
|
w.Write(buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ func StartHTTPServer() {
|
||||||
http.HandleFunc("/blockchain", BlockchainInfoHandler)
|
http.HandleFunc("/blockchain", BlockchainInfoHandler)
|
||||||
http.HandleFunc("/block", BlockHandler)
|
http.HandleFunc("/block", BlockHandler)
|
||||||
http.HandleFunc("/broadcast_tx", BroadcastTxHandler)
|
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")))
|
log.Info(Fmt("Starting RPC HTTP server on %s", config.App.GetString("RPC.HTTP.ListenAddr")))
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ import (
|
||||||
func BroadcastTxHandler(w http.ResponseWriter, r *http.Request) {
|
func BroadcastTxHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
txJSON := GetParam(r, "tx")
|
txJSON := GetParam(r, "tx")
|
||||||
var err error
|
var err error
|
||||||
tx := ReadJSON(struct{ Tx }{}, []byte(txJSON), &err).(struct{ Tx }).Tx
|
var tx Tx
|
||||||
|
ReadJSON(&tx, []byte(txJSON), &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid tx: %v", err))
|
WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid tx: %v", err))
|
||||||
return
|
return
|
||||||
|
|
|
@ -110,7 +110,8 @@ func (s *State) Copy() *State {
|
||||||
|
|
||||||
// The accounts from the TxInputs must either already have
|
// The accounts from the TxInputs must either already have
|
||||||
// account.PubKey.(type) != PubKeyNil, (it must be known),
|
// 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) {
|
func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string]*Account, error) {
|
||||||
accounts := map[string]*Account{}
|
accounts := map[string]*Account{}
|
||||||
for _, in := range ins {
|
for _, in := range ins {
|
||||||
|
@ -132,9 +133,7 @@ func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string]
|
||||||
}
|
}
|
||||||
account.PubKey = in.PubKey
|
account.PubKey = in.PubKey
|
||||||
} else {
|
} else {
|
||||||
if _, isNil := in.PubKey.(PubKeyNil); !isNil {
|
in.PubKey = PubKeyNil{}
|
||||||
return nil, ErrTxRedeclaredPubKey
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
accounts[string(in.Address)] = account
|
accounts[string(in.Address)] = account
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
func TestTxSequence(t *testing.T) {
|
||||||
|
|
||||||
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
|
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
|
||||||
|
|
Loading…
Reference in New Issue