github.com/turingchain2020/turingchain@v1.1.21/system/p2p/dht/protocol/wrapper.go (about)

     1  package protocol
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math/rand"
     7  	"runtime"
     8  	"time"
     9  
    10  	"github.com/libp2p/go-libp2p-core/helpers"
    11  
    12  	"github.com/turingchain2020/turingchain/common/log/log15"
    13  	"github.com/turingchain2020/turingchain/queue"
    14  	types2 "github.com/turingchain2020/turingchain/system/p2p/dht/types"
    15  	"github.com/turingchain2020/turingchain/types"
    16  	"github.com/libp2p/go-libp2p-core/crypto"
    17  	"github.com/libp2p/go-libp2p-core/network"
    18  	protobufCodec "github.com/multiformats/go-multicodec/protobuf"
    19  )
    20  
    21  var log = log15.New("module", "p2p.protocol")
    22  
    23  func init() {
    24  	rand.Seed(time.Now().UnixNano())
    25  }
    26  
    27  // ReadStream reads message from stream.
    28  func ReadStream(data types.Message, stream network.Stream) error {
    29  	decoder := protobufCodec.Multicodec(nil).Decoder(stream)
    30  	err := decoder.Decode(data)
    31  	if err != nil {
    32  		log.Error("ReadStream", "pid", stream.Conn().RemotePeer().Pretty(), "protocolID", stream.Protocol(), "decode err", err)
    33  		return err
    34  	}
    35  	return nil
    36  }
    37  
    38  // WriteStream writes message to stream.
    39  func WriteStream(data types.Message, stream network.Stream) error {
    40  	enc := protobufCodec.Multicodec(nil).Encoder(stream)
    41  	err := enc.Encode(data)
    42  	if err != nil {
    43  		log.Error("WriteStream", "pid", stream.Conn().RemotePeer().Pretty(), "protocolID", stream.Protocol(), "encode err", err)
    44  		return err
    45  	}
    46  	return nil
    47  }
    48  
    49  // CloseStream closes the stream after writing, and waits for the EOF.
    50  func CloseStream(stream network.Stream) {
    51  	if stream == nil {
    52  		return
    53  	}
    54  	go func() {
    55  		err := helpers.FullClose(stream)
    56  		if err != nil {
    57  			//just log it because it dose not matter
    58  			log.Debug("CloseStream", "err", err, "protocol ID", stream.Protocol())
    59  		}
    60  	}()
    61  }
    62  
    63  // AuthenticateMessage authenticates p2p message.
    64  func AuthenticateMessage(message types.Message, stream network.Stream) bool {
    65  	var sign, bin []byte
    66  	// store a temp ref to signature and remove it from message data
    67  	// sign is a string to allow easy reset to zero-value (empty string)
    68  	switch t := message.(type) {
    69  	case *types.P2PRequest:
    70  		sign = t.Headers.Sign
    71  		t.Headers.Sign = nil
    72  		// marshall data without the signature to protobuf3 binary format
    73  		bin = types.Encode(t)
    74  		// restore sig in message data (for possible future use)
    75  		t.Headers.Sign = sign
    76  	case *types.P2PResponse:
    77  		sign = t.Headers.Sign
    78  		t.Headers.Sign = nil
    79  		// marshall data without the signature to protobuf3 binary format
    80  		bin = types.Encode(t)
    81  		// restore sig in message data (for possible future use)
    82  		t.Headers.Sign = sign
    83  	default:
    84  		return false
    85  	}
    86  
    87  	// verify the data was authored by the signing peer identified by the public key
    88  	// and signature included in the message
    89  	return verifyData(bin, sign, stream.Conn().RemotePublicKey())
    90  }
    91  
    92  // Verify incoming p2p message data integrity
    93  // data: data to verify
    94  // signature: author signature provided in the message payload
    95  // pubKey: public key of remote peer
    96  func verifyData(data []byte, signature []byte, pubKey crypto.PubKey) bool {
    97  	res, err := pubKey.Verify(data, signature)
    98  	if err != nil {
    99  		log.Error("Error authenticating data", "err", err)
   100  		return false
   101  	}
   102  
   103  	return res
   104  }
   105  
   106  // ReadStreamAndAuthenticate verifies the message after reading it from the stream.
   107  func ReadStreamAndAuthenticate(message types.Message, stream network.Stream) error {
   108  	if err := ReadStream(message, stream); err != nil {
   109  		return err
   110  	}
   111  	if !AuthenticateMessage(message, stream) {
   112  		return types2.ErrWrongSignature
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  // signProtoMessage signs an outgoing p2p message payload.
   119  func signProtoMessage(message types.Message, stream network.Stream) ([]byte, error) {
   120  	privKey := stream.Conn().LocalPrivateKey()
   121  	return privKey.Sign(types.Encode(message))
   122  }
   123  
   124  // SignAndWriteStream signs the message before writing it to the stream.
   125  func SignAndWriteStream(message types.Message, stream network.Stream) error {
   126  	switch t := message.(type) {
   127  	case *types.P2PRequest:
   128  		t.Headers = &types.P2PMessageHeaders{
   129  			Version:   types2.Version,
   130  			Timestamp: time.Now().Unix(),
   131  			Id:        rand.Int63(),
   132  		}
   133  		sign, err := signProtoMessage(t, stream)
   134  		if err != nil {
   135  			return err
   136  		}
   137  		t.Headers.Sign = sign
   138  	case *types.P2PResponse:
   139  		t.Headers = &types.P2PMessageHeaders{
   140  			Version:   types2.Version,
   141  			Timestamp: time.Now().Unix(),
   142  			Id:        rand.Int63(),
   143  		}
   144  		sign, err := signProtoMessage(t, stream)
   145  		if err != nil {
   146  			return err
   147  		}
   148  		t.Headers.Sign = sign
   149  	default:
   150  		log.Error("SignAndWriteStream wrong message type")
   151  		return types2.ErrInvalidMessageType
   152  	}
   153  	return WriteStream(message, stream)
   154  }
   155  
   156  // HandlerWithClose wraps handler with closing stream and recovering from panic.
   157  func HandlerWithClose(f network.StreamHandler) network.StreamHandler {
   158  	return func(stream network.Stream) {
   159  		defer func() {
   160  			if r := recover(); r != nil {
   161  				log.Error("handle stream", "panic error", r)
   162  				fmt.Println(string(panicTrace(4)))
   163  				_ = stream.Reset()
   164  			}
   165  		}()
   166  		f(stream)
   167  		CloseStream(stream)
   168  	}
   169  }
   170  
   171  // HandlerWithWrite wraps handler with writing, closing stream and recovering from panic.
   172  func HandlerWithWrite(f func(resp *types.P2PResponse) error) network.StreamHandler {
   173  	return func(stream network.Stream) {
   174  		var res types.P2PResponse
   175  		err := f(&res)
   176  		if err != nil {
   177  			res.Response = nil
   178  			res.Error = err.Error()
   179  		}
   180  		res.Headers = &types.P2PMessageHeaders{
   181  			Version:   types2.Version,
   182  			Timestamp: time.Now().Unix(),
   183  			Id:        rand.Int63(),
   184  		}
   185  		if err := WriteStream(&res, stream); err != nil {
   186  			log.Error("HandlerWithWrite", "write stream error", err)
   187  			return
   188  		}
   189  	}
   190  }
   191  
   192  // HandlerWithRead wraps handler with reading, closing stream and recovering from panic.
   193  func HandlerWithRead(f func(request *types.P2PRequest)) network.StreamHandler {
   194  	return func(stream network.Stream) {
   195  		var req types.P2PRequest
   196  		if err := ReadStream(&req, stream); err != nil {
   197  			log.Error("HandlerWithRead", "read stream error", err)
   198  			return
   199  		}
   200  		f(&req)
   201  	}
   202  }
   203  
   204  // HandlerWithAuth wraps HandlerWithRead with authenticating.
   205  func HandlerWithAuth(f func(request *types.P2PRequest)) network.StreamHandler {
   206  	return func(stream network.Stream) {
   207  		var req types.P2PRequest
   208  		if err := ReadStream(&req, stream); err != nil {
   209  			log.Error("HandlerWithAuthAndSign", "read stream error", err)
   210  			return
   211  		}
   212  		if !AuthenticateMessage(&req, stream) {
   213  			return
   214  		}
   215  		f(&req)
   216  	}
   217  }
   218  
   219  // HandlerWithRW wraps handler with reading, writing, closing stream and recovering from panic.
   220  func HandlerWithRW(f func(request *types.P2PRequest, response *types.P2PResponse) error) network.StreamHandler {
   221  	return func(stream network.Stream) {
   222  		var req types.P2PRequest
   223  		if err := ReadStream(&req, stream); err != nil {
   224  			log.Error("HandlerWithRW", "read stream error", err)
   225  			return
   226  		}
   227  		var res types.P2PResponse
   228  		err := f(&req, &res)
   229  		if err != nil {
   230  			res.Response = nil
   231  			res.Error = err.Error()
   232  		}
   233  		res.Headers = &types.P2PMessageHeaders{
   234  			Version:   types2.Version,
   235  			Timestamp: time.Now().Unix(),
   236  			Id:        rand.Int63(),
   237  		}
   238  		if err := WriteStream(&res, stream); err != nil {
   239  			log.Error("HandlerWithAuthAndSign", "write stream error", err)
   240  			return
   241  		}
   242  	}
   243  }
   244  
   245  // HandlerWithAuthAndSign wraps HandlerWithRW with signing and authenticating.
   246  func HandlerWithAuthAndSign(f func(request *types.P2PRequest, response *types.P2PResponse) error) network.StreamHandler {
   247  	return func(stream network.Stream) {
   248  		var req types.P2PRequest
   249  		if err := ReadStream(&req, stream); err != nil {
   250  			log.Error("HandlerWithAuthAndSign", "read stream error", err)
   251  			return
   252  		}
   253  		if !AuthenticateMessage(&req, stream) {
   254  			return
   255  		}
   256  		var res types.P2PResponse
   257  		err := f(&req, &res)
   258  		if err != nil {
   259  			res.Response = nil
   260  			res.Error = err.Error()
   261  		}
   262  		res.Headers = &types.P2PMessageHeaders{
   263  			Version:   types2.Version,
   264  			Timestamp: time.Now().Unix(),
   265  			Id:        rand.Int63(),
   266  		}
   267  		sign, err := signProtoMessage(&res, stream)
   268  		if err != nil {
   269  			log.Error("HandlerWithAuthAndSign", "signProtoMessage error", err)
   270  			return
   271  		}
   272  		res.Headers.Sign = sign
   273  		if err := WriteStream(&res, stream); err != nil {
   274  			log.Error("HandlerWithAuthAndSign", "write stream error", err)
   275  			return
   276  		}
   277  	}
   278  }
   279  
   280  //TODO
   281  // Any developer can define his own stream handler wrapper.
   282  
   283  // EventHandlerWithRecover warps the event handler with recover for catching the panic while processing message.
   284  func EventHandlerWithRecover(f func(m *queue.Message)) func(m *queue.Message) {
   285  	return func(m *queue.Message) {
   286  		defer func() {
   287  			if r := recover(); r != nil {
   288  				log.Error("handle event", "panic error", r)
   289  				fmt.Println(string(panicTrace(4)))
   290  			}
   291  		}()
   292  		f(m)
   293  	}
   294  }
   295  
   296  //TODO
   297  // Any developer can define his own event handler wrapper.
   298  
   299  // panicTrace traces panic stack info.
   300  func panicTrace(kb int) []byte {
   301  	s := []byte("/src/runtime/panic.go")
   302  	e := []byte("\ngoroutine ")
   303  	line := []byte("\n")
   304  	stack := make([]byte, kb<<10) //4KB
   305  	length := runtime.Stack(stack, true)
   306  	start := bytes.Index(stack, s)
   307  	stack = stack[start:length]
   308  	start = bytes.Index(stack, line) + 1
   309  	stack = stack[start:]
   310  	end := bytes.LastIndex(stack, line)
   311  	if end != -1 {
   312  		stack = stack[:end]
   313  	}
   314  	end = bytes.Index(stack, e)
   315  	if end != -1 {
   316  		stack = stack[:end]
   317  	}
   318  	stack = bytes.TrimRight(stack, "\n")
   319  	return stack
   320  }