2021-02-02 09:34:27 +00:00
|
|
|
package stateroot
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2021-03-25 08:55:06 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2021-02-02 09:34:27 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
2021-03-25 08:55:06 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
2021-02-02 09:34:27 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RelayCallback represents callback for sending validated state roots.
|
|
|
|
type RelayCallback = func(*payload.Extensible)
|
|
|
|
|
|
|
|
// AddSignature adds state root signature.
|
|
|
|
func (s *service) AddSignature(height uint32, validatorIndex int32, sig []byte) error {
|
|
|
|
if !s.MainCfg.Enabled {
|
|
|
|
return nil
|
|
|
|
}
|
2021-03-25 08:55:06 +00:00
|
|
|
acc := s.getAccount()
|
|
|
|
if acc == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2021-02-02 09:34:27 +00:00
|
|
|
|
|
|
|
pubs := s.GetStateValidators(height)
|
|
|
|
if validatorIndex < 0 || int(validatorIndex) >= len(pubs) {
|
|
|
|
return errors.New("invalid validator index")
|
|
|
|
}
|
|
|
|
pub := pubs[validatorIndex]
|
|
|
|
|
|
|
|
incRoot := s.getIncompleteRoot(height)
|
|
|
|
if incRoot == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
incRoot.Lock()
|
|
|
|
if incRoot.root != nil {
|
2021-03-25 19:11:55 +00:00
|
|
|
ok := pub.VerifyHashable(sig, uint32(s.Network), incRoot.root)
|
2021-02-02 09:34:27 +00:00
|
|
|
if !ok {
|
|
|
|
incRoot.Unlock()
|
|
|
|
return fmt.Errorf("invalid state root signature for %d", validatorIndex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
incRoot.addSignature(pub, sig)
|
|
|
|
sr, ready := incRoot.finalize(pubs)
|
|
|
|
incRoot.Unlock()
|
|
|
|
|
|
|
|
if ready {
|
|
|
|
err := s.AddStateRoot(sr)
|
|
|
|
if err != nil {
|
|
|
|
s.log.Error("can't add validated state root", zap.Error(err))
|
|
|
|
}
|
2021-03-25 08:55:06 +00:00
|
|
|
s.sendValidatedRoot(sr, acc.PrivateKey())
|
2021-02-02 09:34:27 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetConfig returns service configuration.
|
|
|
|
func (s *service) GetConfig() config.StateRoot {
|
|
|
|
return s.MainCfg
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getIncompleteRoot(height uint32) *incompleteRoot {
|
|
|
|
s.srMtx.Lock()
|
|
|
|
defer s.srMtx.Unlock()
|
|
|
|
if incRoot, ok := s.incompleteRoots[height]; ok {
|
|
|
|
return incRoot
|
|
|
|
}
|
|
|
|
incRoot := &incompleteRoot{sigs: make(map[string]*rootSig)}
|
|
|
|
s.incompleteRoots[height] = incRoot
|
|
|
|
return incRoot
|
|
|
|
}
|
|
|
|
|
2021-03-25 08:55:06 +00:00
|
|
|
func (s *service) sendValidatedRoot(r *state.MPTRoot, priv *keys.PrivateKey) {
|
2021-02-02 09:34:27 +00:00
|
|
|
w := io.NewBufBinWriter()
|
2021-03-25 19:11:55 +00:00
|
|
|
m := NewMessage(RootT, r)
|
2021-02-02 09:34:27 +00:00
|
|
|
m.EncodeBinary(w.BinWriter)
|
|
|
|
ep := &payload.Extensible{
|
2021-03-26 19:16:46 +00:00
|
|
|
Category: Category,
|
2021-02-02 09:34:27 +00:00
|
|
|
ValidBlockStart: r.Index,
|
|
|
|
ValidBlockEnd: r.Index + transaction.MaxValidUntilBlockIncrement,
|
2021-03-25 08:55:06 +00:00
|
|
|
Sender: priv.GetScriptHash(),
|
2021-02-02 09:34:27 +00:00
|
|
|
Data: w.Bytes(),
|
2021-03-25 08:55:06 +00:00
|
|
|
Witness: transaction.Witness{
|
|
|
|
VerificationScript: s.getAccount().GetVerificationScript(),
|
|
|
|
},
|
2021-02-02 09:34:27 +00:00
|
|
|
}
|
2021-03-25 18:59:54 +00:00
|
|
|
sig := priv.SignHashable(uint32(s.Network), ep)
|
2021-03-25 08:55:06 +00:00
|
|
|
buf := io.NewBufBinWriter()
|
|
|
|
emit.Bytes(buf.BinWriter, sig)
|
|
|
|
ep.Witness.InvocationScript = buf.Bytes()
|
2021-02-02 09:34:27 +00:00
|
|
|
s.getRelayCallback()(ep)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) getRelayCallback() RelayCallback {
|
|
|
|
s.cbMtx.RLock()
|
|
|
|
defer s.cbMtx.RUnlock()
|
|
|
|
return s.onValidatedRoot
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetRelayCallback sets callback to pool and broadcast tx.
|
|
|
|
func (s *service) SetRelayCallback(cb RelayCallback) {
|
|
|
|
s.cbMtx.Lock()
|
|
|
|
defer s.cbMtx.Unlock()
|
|
|
|
s.onValidatedRoot = cb
|
|
|
|
}
|