forked from ava-labs/subnet-evm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
service.go
131 lines (115 loc) · 4.75 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package warp
import (
"context"
"errors"
"fmt"
"github.com/MetalBlockchain/metalgo/ids"
"github.com/MetalBlockchain/metalgo/vms/platformvm/warp"
"github.com/MetalBlockchain/metalgo/vms/platformvm/warp/payload"
"github.com/MetalBlockchain/subnet-evm/peer"
"github.com/MetalBlockchain/subnet-evm/warp/aggregator"
"github.com/MetalBlockchain/subnet-evm/warp/validators"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
)
var errNoValidators = errors.New("cannot aggregate signatures from subnet with no validators")
// API introduces snowman specific functionality to the evm
type API struct {
networkID uint32
sourceSubnetID, sourceChainID ids.ID
backend Backend
state *validators.State
client peer.NetworkClient
}
func NewAPI(networkID uint32, sourceSubnetID ids.ID, sourceChainID ids.ID, state *validators.State, backend Backend, client peer.NetworkClient) *API {
return &API{
networkID: networkID,
sourceSubnetID: sourceSubnetID,
sourceChainID: sourceChainID,
backend: backend,
state: state,
client: client,
}
}
// GetMessage returns the Warp message associated with a messageID.
func (a *API) GetMessage(ctx context.Context, messageID ids.ID) (hexutil.Bytes, error) {
message, err := a.backend.GetMessage(messageID)
if err != nil {
return nil, fmt.Errorf("failed to get message %s with error %w", messageID, err)
}
return hexutil.Bytes(message.Bytes()), nil
}
// GetMessageSignature returns the BLS signature associated with a messageID.
func (a *API) GetMessageSignature(ctx context.Context, messageID ids.ID) (hexutil.Bytes, error) {
signature, err := a.backend.GetMessageSignature(messageID)
if err != nil {
return nil, fmt.Errorf("failed to get signature for message %s with error %w", messageID, err)
}
return signature[:], nil
}
// GetBlockSignature returns the BLS signature associated with a blockID.
func (a *API) GetBlockSignature(ctx context.Context, blockID ids.ID) (hexutil.Bytes, error) {
signature, err := a.backend.GetBlockSignature(blockID)
if err != nil {
return nil, fmt.Errorf("failed to get signature for block %s with error %w", blockID, err)
}
return signature[:], nil
}
// GetMessageAggregateSignature fetches the aggregate signature for the requested [messageID]
func (a *API) GetMessageAggregateSignature(ctx context.Context, messageID ids.ID, quorumNum uint64, subnetIDStr string) (signedMessageBytes hexutil.Bytes, err error) {
unsignedMessage, err := a.backend.GetMessage(messageID)
if err != nil {
return nil, err
}
return a.aggregateSignatures(ctx, unsignedMessage, quorumNum, subnetIDStr)
}
// GetBlockAggregateSignature fetches the aggregate signature for the requested [blockID]
func (a *API) GetBlockAggregateSignature(ctx context.Context, blockID ids.ID, quorumNum uint64, subnetIDStr string) (signedMessageBytes hexutil.Bytes, err error) {
blockHashPayload, err := payload.NewHash(blockID)
if err != nil {
return nil, err
}
unsignedMessage, err := warp.NewUnsignedMessage(a.networkID, a.sourceChainID, blockHashPayload.Bytes())
if err != nil {
return nil, err
}
return a.aggregateSignatures(ctx, unsignedMessage, quorumNum, subnetIDStr)
}
func (a *API) aggregateSignatures(ctx context.Context, unsignedMessage *warp.UnsignedMessage, quorumNum uint64, subnetIDStr string) (hexutil.Bytes, error) {
subnetID := a.sourceSubnetID
if len(subnetIDStr) > 0 {
sid, err := ids.FromString(subnetIDStr)
if err != nil {
return nil, fmt.Errorf("failed to parse subnetID: %q", subnetIDStr)
}
subnetID = sid
}
pChainHeight, err := a.state.GetCurrentHeight(ctx)
if err != nil {
return nil, err
}
validators, totalWeight, err := warp.GetCanonicalValidatorSet(ctx, a.state, pChainHeight, subnetID)
if err != nil {
return nil, fmt.Errorf("failed to get validator set: %w", err)
}
if len(validators) == 0 {
return nil, fmt.Errorf("%w (SubnetID: %s, Height: %d)", errNoValidators, subnetID, pChainHeight)
}
log.Debug("Fetching signature",
"sourceSubnetID", subnetID,
"height", pChainHeight,
"numValidators", len(validators),
"totalWeight", totalWeight,
)
agg := aggregator.New(aggregator.NewSignatureGetter(a.client), validators, totalWeight)
signatureResult, err := agg.AggregateSignatures(ctx, unsignedMessage, quorumNum)
if err != nil {
return nil, err
}
// TODO: return the signature and total weight as well to the caller for more complete details
// Need to decide on the best UI for this and write up documentation with the potential
// gotchas that could impact signed messages becoming invalid.
return hexutil.Bytes(signatureResult.Message.Bytes()), nil
}