Merge pull request #588 from ethersphere/frontier/SEC-29
Frontier/sec 29
This commit is contained in:
commit
4a4da9a24e
|
@ -148,6 +148,23 @@ func NewBlockWithHeader(header *Header) *Block {
|
||||||
return &Block{header: header}
|
return &Block{header: header}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Block) ValidateFields() error {
|
||||||
|
if self.header == nil {
|
||||||
|
return fmt.Errorf("header is nil")
|
||||||
|
}
|
||||||
|
for i, transaction := range self.transactions {
|
||||||
|
if transaction == nil {
|
||||||
|
return fmt.Errorf("transaction %d is nil", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, uncle := range self.uncles {
|
||||||
|
if uncle == nil {
|
||||||
|
return fmt.Errorf("uncle %d is nil", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Block) DecodeRLP(s *rlp.Stream) error {
|
func (self *Block) DecodeRLP(s *rlp.Stream) error {
|
||||||
var eb extblock
|
var eb extblock
|
||||||
if err := s.Decode(&eb); err != nil {
|
if err := s.Decode(&eb); err != nil {
|
||||||
|
|
|
@ -185,7 +185,10 @@ func (self *ethProtocol) handle() error {
|
||||||
if err := msg.Decode(&txs); err != nil {
|
if err := msg.Decode(&txs); err != nil {
|
||||||
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
for _, tx := range txs {
|
for i, tx := range txs {
|
||||||
|
if tx == nil {
|
||||||
|
return self.protoError(ErrDecode, "transaction %d is nil", i)
|
||||||
|
}
|
||||||
jsonlogger.LogJson(&logger.EthTxReceived{
|
jsonlogger.LogJson(&logger.EthTxReceived{
|
||||||
TxHash: tx.Hash().Hex(),
|
TxHash: tx.Hash().Hex(),
|
||||||
RemoteId: self.peer.ID().String(),
|
RemoteId: self.peer.ID().String(),
|
||||||
|
@ -268,6 +271,9 @@ func (self *ethProtocol) handle() error {
|
||||||
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := block.ValidateFields(); err != nil {
|
||||||
|
return self.protoError(ErrDecode, "block validation %v: %v", msg, err)
|
||||||
|
}
|
||||||
self.blockPool.AddBlock(&block, self.id)
|
self.blockPool.AddBlock(&block, self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,6 +282,9 @@ func (self *ethProtocol) handle() error {
|
||||||
if err := msg.Decode(&request); err != nil {
|
if err := msg.Decode(&request); err != nil {
|
||||||
return self.protoError(ErrDecode, "%v: %v", msg, err)
|
return self.protoError(ErrDecode, "%v: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
if err := request.Block.ValidateFields(); err != nil {
|
||||||
|
return self.protoError(ErrDecode, "block validation %v: %v", msg, err)
|
||||||
|
}
|
||||||
hash := request.Block.Hash()
|
hash := request.Block.Hash()
|
||||||
_, chainHead, _ := self.chainManager.Status()
|
_, chainHead, _ := self.chainManager.Status()
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,10 @@ func (self *testChainManager) GetBlockHashesFromHash(hash common.Hash, amount ui
|
||||||
func (self *testChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
|
func (self *testChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
|
||||||
if self.status != nil {
|
if self.status != nil {
|
||||||
td, currentBlock, genesisBlock = self.status()
|
td, currentBlock, genesisBlock = self.status()
|
||||||
|
} else {
|
||||||
|
td = common.Big1
|
||||||
|
currentBlock = common.Hash{1}
|
||||||
|
genesisBlock = common.Hash{2}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -163,14 +167,29 @@ func (self *ethProtocolTester) run() {
|
||||||
self.quit <- err
|
self.quit <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ethProtocolTester) handshake(t *testing.T, mock bool) {
|
||||||
|
td, currentBlock, genesis := self.chainManager.Status()
|
||||||
|
// first outgoing msg should be StatusMsg.
|
||||||
|
err := p2p.ExpectMsg(self, StatusMsg, &statusMsgData{
|
||||||
|
ProtocolVersion: ProtocolVersion,
|
||||||
|
NetworkId: NetworkId,
|
||||||
|
TD: td,
|
||||||
|
CurrentBlock: currentBlock,
|
||||||
|
GenesisBlock: genesis,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("incorrect outgoing status: %v", err)
|
||||||
|
}
|
||||||
|
if mock {
|
||||||
|
go p2p.Send(self, StatusMsg, &statusMsgData{ProtocolVersion, NetworkId, td, currentBlock, genesis})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatusMsgErrors(t *testing.T) {
|
func TestStatusMsgErrors(t *testing.T) {
|
||||||
logInit()
|
logInit()
|
||||||
eth := newEth(t)
|
eth := newEth(t)
|
||||||
td := common.Big1
|
|
||||||
currentBlock := common.Hash{1}
|
|
||||||
genesis := common.Hash{2}
|
|
||||||
eth.chainManager.status = func() (*big.Int, common.Hash, common.Hash) { return td, currentBlock, genesis }
|
|
||||||
go eth.run()
|
go eth.run()
|
||||||
|
td, currentBlock, genesis := eth.chainManager.Status()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
code uint64
|
code uint64
|
||||||
|
@ -195,18 +214,7 @@ func TestStatusMsgErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
// first outgoing msg should be StatusMsg.
|
eth.handshake(t, false)
|
||||||
err := p2p.ExpectMsg(eth, StatusMsg, &statusMsgData{
|
|
||||||
ProtocolVersion: ProtocolVersion,
|
|
||||||
NetworkId: NetworkId,
|
|
||||||
TD: td,
|
|
||||||
CurrentBlock: currentBlock,
|
|
||||||
GenesisBlock: genesis,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("incorrect outgoing status: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the send call might hang until reset because
|
// the send call might hang until reset because
|
||||||
// the protocol might not read the payload.
|
// the protocol might not read the payload.
|
||||||
go p2p.Send(eth, test.code, test.data)
|
go p2p.Send(eth, test.code, test.data)
|
||||||
|
@ -216,3 +224,177 @@ func TestStatusMsgErrors(t *testing.T) {
|
||||||
go eth.run()
|
go eth.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewBlockMsg(t *testing.T) {
|
||||||
|
// logInit()
|
||||||
|
eth := newEth(t)
|
||||||
|
|
||||||
|
var disconnected bool
|
||||||
|
eth.blockPool.removePeer = func(peerId string) {
|
||||||
|
disconnected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
go eth.run()
|
||||||
|
|
||||||
|
eth.handshake(t, true)
|
||||||
|
err := p2p.ExpectMsg(eth, TxMsg, []interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("transactions expected, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tds = make(chan *big.Int)
|
||||||
|
eth.blockPool.addPeer = func(td *big.Int, currentBlock common.Hash, peerId string, requestHashes func(common.Hash) error, requestBlocks func([]common.Hash) error, peerError func(*errs.Error)) (best bool, suspended bool) {
|
||||||
|
tds <- td
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var delay = 1 * time.Second
|
||||||
|
// eth.reset()
|
||||||
|
block := types.NewBlock(common.Hash{1}, common.Address{1}, common.Hash{1}, common.Big1, 1, "extra")
|
||||||
|
|
||||||
|
go p2p.Send(eth, NewBlockMsg, &newBlockMsgData{Block: block})
|
||||||
|
timer := time.After(delay)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case td := <-tds:
|
||||||
|
if td.Cmp(common.Big0) != 0 {
|
||||||
|
t.Errorf("incorrect td %v, expected %v", td, common.Big0)
|
||||||
|
}
|
||||||
|
case <-timer:
|
||||||
|
t.Errorf("no td recorded after %v", delay)
|
||||||
|
return
|
||||||
|
case err := <-eth.quit:
|
||||||
|
t.Errorf("no error expected, got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go p2p.Send(eth, NewBlockMsg, &newBlockMsgData{block, common.Big2})
|
||||||
|
timer = time.After(delay)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case td := <-tds:
|
||||||
|
if td.Cmp(common.Big2) != 0 {
|
||||||
|
t.Errorf("incorrect td %v, expected %v", td, common.Big2)
|
||||||
|
}
|
||||||
|
case <-timer:
|
||||||
|
t.Errorf("no td recorded after %v", delay)
|
||||||
|
return
|
||||||
|
case err := <-eth.quit:
|
||||||
|
t.Errorf("no error expected, got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go p2p.Send(eth, NewBlockMsg, []interface{}{})
|
||||||
|
// Block.DecodeRLP: validation failed: header is nil
|
||||||
|
eth.checkError(ErrDecode, delay)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlockMsg(t *testing.T) {
|
||||||
|
// logInit()
|
||||||
|
eth := newEth(t)
|
||||||
|
blocks := make(chan *types.Block)
|
||||||
|
eth.blockPool.addBlock = func(block *types.Block, peerId string) (err error) {
|
||||||
|
blocks <- block
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var disconnected bool
|
||||||
|
eth.blockPool.removePeer = func(peerId string) {
|
||||||
|
disconnected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
go eth.run()
|
||||||
|
|
||||||
|
eth.handshake(t, true)
|
||||||
|
err := p2p.ExpectMsg(eth, TxMsg, []interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("transactions expected, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var delay = 3 * time.Second
|
||||||
|
// eth.reset()
|
||||||
|
newblock := func(i int64) *types.Block {
|
||||||
|
return types.NewBlock(common.Hash{byte(i)}, common.Address{byte(i)}, common.Hash{byte(i)}, big.NewInt(i), uint64(i), string(i))
|
||||||
|
}
|
||||||
|
b := newblock(0)
|
||||||
|
b.Header().Difficulty = nil // check if nil as *big.Int decodes as 0
|
||||||
|
go p2p.Send(eth, BlocksMsg, types.Blocks{b, newblock(1), newblock(2)})
|
||||||
|
timer := time.After(delay)
|
||||||
|
for i := int64(0); i < 3; i++ {
|
||||||
|
select {
|
||||||
|
case block := <-blocks:
|
||||||
|
if (block.ParentHash() != common.Hash{byte(i)}) {
|
||||||
|
t.Errorf("incorrect block %v, expected %v", block.ParentHash(), common.Hash{byte(i)})
|
||||||
|
}
|
||||||
|
if block.Difficulty().Cmp(big.NewInt(i)) != 0 {
|
||||||
|
t.Errorf("incorrect block %v, expected %v", block.Difficulty(), big.NewInt(i))
|
||||||
|
}
|
||||||
|
case <-timer:
|
||||||
|
t.Errorf("no td recorded after %v", delay)
|
||||||
|
return
|
||||||
|
case err := <-eth.quit:
|
||||||
|
t.Errorf("no error expected, got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go p2p.Send(eth, BlocksMsg, []interface{}{[]interface{}{}})
|
||||||
|
eth.checkError(ErrDecode, delay)
|
||||||
|
if !disconnected {
|
||||||
|
t.Errorf("peer not disconnected after error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test empty transaction
|
||||||
|
eth.reset()
|
||||||
|
go eth.run()
|
||||||
|
eth.handshake(t, true)
|
||||||
|
err = p2p.ExpectMsg(eth, TxMsg, []interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("transactions expected, got %v", err)
|
||||||
|
}
|
||||||
|
b = newblock(0)
|
||||||
|
b.AddTransaction(nil)
|
||||||
|
go p2p.Send(eth, BlocksMsg, types.Blocks{b})
|
||||||
|
eth.checkError(ErrDecode, delay)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTransactionsMsg(t *testing.T) {
|
||||||
|
logInit()
|
||||||
|
eth := newEth(t)
|
||||||
|
txs := make(chan *types.Transaction)
|
||||||
|
|
||||||
|
eth.txPool.addTransactions = func(t []*types.Transaction) {
|
||||||
|
for _, tx := range t {
|
||||||
|
txs <- tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go eth.run()
|
||||||
|
|
||||||
|
eth.handshake(t, true)
|
||||||
|
err := p2p.ExpectMsg(eth, TxMsg, []interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("transactions expected, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var delay = 3 * time.Second
|
||||||
|
tx := &types.Transaction{}
|
||||||
|
|
||||||
|
go p2p.Send(eth, TxMsg, []interface{}{tx, tx})
|
||||||
|
timer := time.After(delay)
|
||||||
|
for i := int64(0); i < 2; i++ {
|
||||||
|
select {
|
||||||
|
case <-txs:
|
||||||
|
case <-timer:
|
||||||
|
return
|
||||||
|
case err := <-eth.quit:
|
||||||
|
t.Errorf("no error expected, got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go p2p.Send(eth, TxMsg, []interface{}{[]interface{}{}})
|
||||||
|
eth.checkError(ErrDecode, delay)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue