Skip to content

Commit

Permalink
add p2p handler for acp-118 sig request (#1260)
Browse files Browse the repository at this point in the history
* add p2p handler for acp-118 sig request

* update to use addressed payload and hash types from avago

* add future TODOs

* nit: log unexpected type in error

* nit
  • Loading branch information
darioush authored Aug 2, 2024
1 parent 07d0a9c commit f25838f
Show file tree
Hide file tree
Showing 7 changed files with 406 additions and 4 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.21.12

require (
github.com/VictoriaMetrics/fastcache v1.12.1
github.com/ava-labs/avalanchego v1.11.10
github.com/ava-labs/avalanchego v1.11.11-0.20240729205337-a0f7e422bb84
github.com/cespare/cp v0.1.0
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
github.com/davecgh/go-spew v1.1.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/ava-labs/avalanchego v1.11.10 h1:QujciF5OEp5FwAoe/RciFF/i47rxU5rkEr6fVuUBS1Q=
github.com/ava-labs/avalanchego v1.11.10/go.mod h1:POgZPryqe80OeHCDNrXrPOKoFre736iFuMgmUBeKaLc=
github.com/ava-labs/avalanchego v1.11.11-0.20240729205337-a0f7e422bb84 h1:AmPZLlnVREbJ/viK/hDTIVn1bqX8QTB2CFtrBxHwnsw=
github.com/ava-labs/avalanchego v1.11.11-0.20240729205337-a0f7e422bb84/go.mod h1:POgZPryqe80OeHCDNrXrPOKoFre736iFuMgmUBeKaLc=
github.com/ava-labs/coreth v0.13.7 h1:k8T9u/ROifl8f7oXjHRc1KvSISRl9txvy7gGVmHEz6g=
github.com/ava-labs/coreth v0.13.7/go.mod h1:tXDujonxXFOF6oK5HS2EmgtSXJK3Gy6RpZxb5WzR9rM=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
Expand Down
11 changes: 8 additions & 3 deletions plugin/evm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/ava-labs/subnet-evm/sync/client/stats"
"github.com/ava-labs/subnet-evm/trie"
"github.com/ava-labs/subnet-evm/warp"
"github.com/ava-labs/subnet-evm/warp/handlers"
warpValidators "github.com/ava-labs/subnet-evm/warp/validators"

// Force-load tracer engine to trigger registration
Expand Down Expand Up @@ -513,7 +514,7 @@ func (vm *VM) Initialize(

go vm.ctx.Log.RecoverAndPanic(vm.startContinuousProfiler)

vm.initializeStateSyncServer()
vm.initializeHandlers()
return vm.initializeStateSyncClient(lastAcceptedHeight)
}

Expand Down Expand Up @@ -616,13 +617,17 @@ func (vm *VM) initializeStateSyncClient(lastAcceptedHeight uint64) error {
return nil
}

// initializeStateSyncServer should be called after [vm.chain] is initialized.
func (vm *VM) initializeStateSyncServer() {
// initializeHandlers should be called after [vm.chain] is initialized.
func (vm *VM) initializeHandlers() {
vm.StateSyncServer = NewStateSyncServer(&stateSyncServerConfig{
Chain: vm.blockChain,
SyncableInterval: vm.config.StateSyncCommitInterval,
})

// Add p2p warp message warpHandler
warpHandler := handlers.NewSignatureRequestHandlerP2P(vm.warpBackend, vm.networkCodec)
vm.Network.AddHandler(p2p.SignatureRequestHandlerID, warpHandler)

vm.setAppRequestHandlers()
vm.setCrossChainAppRequestHandler()
}
Expand Down
2 changes: 2 additions & 0 deletions warp/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type Backend interface {
GetBlockSignature(blockID ids.ID) ([bls.SignatureLen]byte, error)

// GetMessage retrieves the [unsignedMessage] from the warp backend database if available
// TODO: After E-Upgrade, the backend no longer needs to store the mapping from messageHash
// to unsignedMessage (and this method can be removed).
GetMessage(messageHash ids.ID) (*avalancheWarp.UnsignedMessage, error)

// Clear clears the entire db
Expand Down
1 change: 1 addition & 0 deletions warp/handlers/signature_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
)

// SignatureRequestHandler serves warp signature requests. It is a peer.RequestHandler for message.MessageSignatureRequest.
// TODO: After E-Upgrade, this handler can be removed and SignatureRequestHandlerP2P is sufficient.
type SignatureRequestHandler struct {
backend warp.Backend
codec codec.Manager
Expand Down
160 changes: 160 additions & 0 deletions warp/handlers/signature_request_p2p.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package handlers

import (
"context"
"fmt"
"time"

"github.com/ava-labs/avalanchego/codec"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/network/p2p"
"github.com/ava-labs/avalanchego/proto/pb/sdk"
"github.com/ava-labs/avalanchego/snow/engine/common"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp"
"github.com/ava-labs/avalanchego/vms/platformvm/warp/payload"
"github.com/ava-labs/subnet-evm/warp"
"google.golang.org/protobuf/proto"
)

var _ p2p.Handler = (*SignatureRequestHandlerP2P)(nil)

const (
ErrFailedToParse = iota
ErrFailedToGetSig
ErrFailedToMarshal
)

// SignatureRequestHandlerP2P serves warp signature requests using the p2p
// framework from avalanchego. It is a peer.RequestHandler for
// message.MessageSignatureRequest.
type SignatureRequestHandlerP2P struct {
backend warp.Backend
codec codec.Manager
stats *handlerStats
}

func NewSignatureRequestHandlerP2P(backend warp.Backend, codec codec.Manager) *SignatureRequestHandlerP2P {
return &SignatureRequestHandlerP2P{
backend: backend,
codec: codec,
stats: newStats(),
}
}

func (s *SignatureRequestHandlerP2P) AppRequest(
ctx context.Context,
nodeID ids.NodeID,
deadline time.Time,
requestBytes []byte,
) ([]byte, *common.AppError) {
// Per ACP-118, the requestBytes are the serialized form of
// sdk.SignatureRequest.
req := new(sdk.SignatureRequest)
if err := proto.Unmarshal(requestBytes, req); err != nil {
return nil, &common.AppError{
Code: ErrFailedToParse,
Message: "failed to unmarshal request: " + err.Error(),
}
}

unsignedMessage, err := avalancheWarp.ParseUnsignedMessage(req.Message)
if err != nil {
return nil, &common.AppError{
Code: ErrFailedToParse,
Message: "failed to parse unsigned message: " + err.Error(),
}
}
parsed, err := payload.Parse(unsignedMessage.Payload)
if err != nil {
return nil, &common.AppError{
Code: ErrFailedToParse,
Message: "failed to parse payload: " + err.Error(),
}
}

var sig [bls.SignatureLen]byte
switch p := parsed.(type) {
case *payload.AddressedCall:
// Note we pass the unsigned message ID to GetMessageSignature since
// that is what the backend expects.
// However, we verify the types and format of the payload to ensure
// the message conforms to the ACP-118 spec.
sig, err = s.GetMessageSignature(unsignedMessage.ID())
if err != nil {
s.stats.IncMessageSignatureMiss()
} else {
s.stats.IncMessageSignatureHit()
}
case *payload.Hash:
sig, err = s.GetBlockSignature(p.Hash)
if err != nil {
s.stats.IncBlockSignatureMiss()
} else {
s.stats.IncBlockSignatureHit()
}
default:
return nil, &common.AppError{
Code: ErrFailedToParse,
Message: fmt.Sprintf("unknown payload type: %T", p),
}
}
if err != nil {
return nil, &common.AppError{
Code: ErrFailedToGetSig,
Message: "failed to get signature: " + err.Error(),
}
}

// Per ACP-118, the responseBytes are the serialized form of
// sdk.SignatureResponse.
resp := &sdk.SignatureResponse{Signature: sig[:]}
respBytes, err := proto.Marshal(resp)
if err != nil {
return nil, &common.AppError{
Code: ErrFailedToMarshal,
Message: "failed to marshal response: " + err.Error(),
}
}
return respBytes, nil
}

func (s *SignatureRequestHandlerP2P) GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, error) {
startTime := time.Now()
s.stats.IncMessageSignatureRequest()

// Always report signature request time
defer func() {
s.stats.UpdateMessageSignatureRequestTime(time.Since(startTime))
}()

return s.backend.GetMessageSignature(messageID)
}

func (s *SignatureRequestHandlerP2P) GetBlockSignature(blockID ids.ID) ([bls.SignatureLen]byte, error) {
startTime := time.Now()
s.stats.IncBlockSignatureRequest()

// Always report signature request time
defer func() {
s.stats.UpdateBlockSignatureRequestTime(time.Since(startTime))
}()

return s.backend.GetBlockSignature(blockID)
}

func (s *SignatureRequestHandlerP2P) CrossChainAppRequest(
ctx context.Context,
chainID ids.ID,
deadline time.Time,
requestBytes []byte,
) ([]byte, error) {
return nil, nil
}

func (s *SignatureRequestHandlerP2P) AppGossip(
ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) {
}
Loading

0 comments on commit f25838f

Please sign in to comment.