code.vegaprotocol.io/vega@v0.79.0/vegatools/checkpoint/type.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package checkpoint
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/hex"
    21  	"encoding/json"
    22  	"fmt"
    23  	"strings"
    24  	"time"
    25  
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/protos/vega"
    28  	checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1"
    29  	events "code.vegaprotocol.io/vega/protos/vega/events/v1"
    30  
    31  	"github.com/golang/protobuf/jsonpb"
    32  	"github.com/golang/protobuf/proto"
    33  	"golang.org/x/crypto/sha3"
    34  	"google.golang.org/protobuf/reflect/protoreflect"
    35  )
    36  
    37  type all struct {
    38  	messages map[string]proto.Message
    39  }
    40  
    41  func newAll() *all {
    42  	return &all{
    43  		messages: map[string]proto.Message{
    44  			"governance":         &checkpoint.Proposals{},
    45  			"assets":             &checkpoint.Assets{},
    46  			"collateral":         &checkpoint.Collateral{},
    47  			"network_parameters": &checkpoint.NetParams{},
    48  			"delegation":         &checkpoint.Delegate{},
    49  			"epoch":              &events.EpochEvent{},
    50  			"block":              &checkpoint.Block{},
    51  			"rewards":            &checkpoint.Rewards{},
    52  			"banking":            &checkpoint.Banking{},
    53  			"validators":         &checkpoint.Validators{},
    54  			"staking":            &checkpoint.Staking{},
    55  			"multisig_control":   &checkpoint.MultisigControl{},
    56  			"market_tracker":     &checkpoint.MarketTracker{},
    57  			"execution":          &checkpoint.ExecutionState{},
    58  		},
    59  	}
    60  }
    61  
    62  type allJSON map[string]json.RawMessage
    63  
    64  // AssetErr a convenience error type.
    65  type AssetErr []error
    66  
    67  func (a *all) CheckAssetsCollateral() error {
    68  	assets, ok := a.messages["assets"].(*checkpoint.Assets)
    69  	if !ok {
    70  		return fmt.Errorf("assets not found")
    71  	}
    72  
    73  	assetIDSet := make(map[string]struct{}, len(assets.Assets))
    74  	for _, ass := range assets.Assets {
    75  		assetIDSet[ass.Id] = struct{}{}
    76  	}
    77  
    78  	cAssets := make(map[string]struct{}, len(assetIDSet)) // should be no more than total assets
    79  	for _, c := range a.messages["collateral"].(*checkpoint.Collateral).Balances {
    80  		cAssets[c.Asset] = struct{}{}
    81  	}
    82  
    83  	var errs []error
    84  
    85  	for ca := range cAssets {
    86  		if _, ok := assetIDSet[ca]; !ok {
    87  			errs = append(errs, fmt.Errorf("collateral contains '%s' asset, asset checkpoint does not", ca))
    88  		}
    89  	}
    90  
    91  	if len(errs) != 0 {
    92  		return AssetErr(errs)
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  func (a *all) JSON() ([]byte, error) {
    99  	// format nicely
   100  	marshaler := jsonpb.Marshaler{
   101  		Indent: "   ",
   102  	}
   103  
   104  	allJsn := allJSON{}
   105  
   106  	for k, v := range a.messages {
   107  		var buf bytes.Buffer
   108  		if err := marshaler.Marshal(&buf, v); err != nil {
   109  			return nil, err
   110  		}
   111  		allJsn[k] = buf.Bytes()
   112  	}
   113  
   114  	b, err := json.MarshalIndent(allJsn, "", "   ")
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return b, nil
   119  }
   120  
   121  // fromJSON can be used in the future to load JSON input and generate a checkpoint file.
   122  func fromJSON(in []byte) (*all, error) {
   123  	allJsn := allJSON{}
   124  	if err := json.Unmarshal(in, &allJsn); err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	a := newAll()
   129  
   130  	for k, v := range allJsn {
   131  		reader := bytes.NewReader(v)
   132  		if err := jsonpb.Unmarshal(reader, a.messages[k]); err != nil {
   133  			return nil, err
   134  		}
   135  	}
   136  
   137  	return a, nil
   138  }
   139  
   140  // hash returns the hash for a checkpoint (from core repo - needs to be kept in sync).
   141  func hash(data []byte) string {
   142  	h := sha3.New256()
   143  	_, _ = h.Write(data)
   144  	return hex.EncodeToString(h.Sum(nil))
   145  }
   146  
   147  func allBytes(cp *checkpoint.Checkpoint) []byte {
   148  	buf := types.NewCheckpointFromProto(cp).HashBytes()
   149  	return buf.Bytes()
   150  }
   151  
   152  func (a *all) CheckpointData() ([]byte, string, error) {
   153  	if len(a.messages) == 0 {
   154  		return nil, "", fmt.Errorf("no checkpoint data found")
   155  	}
   156  
   157  	cp := &checkpoint.Checkpoint{
   158  		Governance:        []byte{0},
   159  		Assets:            []byte{0},
   160  		Collateral:        []byte{0},
   161  		NetworkParameters: []byte{0},
   162  		Delegation:        []byte{0},
   163  		Epoch:             []byte{0},
   164  		Block:             []byte{0},
   165  		Rewards:           []byte{0},
   166  		Banking:           []byte{0},
   167  		Validators:        []byte{0},
   168  		Staking:           []byte{0},
   169  		MultisigControl:   []byte{0},
   170  		MarketTracker:     []byte{0},
   171  	}
   172  	cp.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
   173  		name := string(fd.Name())
   174  		v, ok := a.messages[name]
   175  		if !ok {
   176  			return true
   177  		}
   178  		msg, err := proto.Marshal(v)
   179  		if err != nil {
   180  			return true
   181  		}
   182  		cp.ProtoReflect().Set(fd, protoreflect.ValueOf(msg))
   183  		return true
   184  	})
   185  
   186  	ret, err := proto.Marshal(cp)
   187  	if err != nil {
   188  		return nil, "", err
   189  	}
   190  
   191  	if len(ret) == 0 {
   192  		return nil, "", fmt.Errorf("failed to parse checkpoint data")
   193  	}
   194  
   195  	return ret, hash(allBytes(cp)), nil
   196  }
   197  
   198  // Error outputs the mismatches in an easy to read way.
   199  func (a AssetErr) Error() string {
   200  	out := make([]string, 0, len(a)+1)
   201  	out = append(out, "unexpected asset/collateral data found:")
   202  	for _, e := range a {
   203  		out = append(out, fmt.Sprintf("\t%s", e.Error()))
   204  	}
   205  	return strings.Join(out, "\n")
   206  }
   207  
   208  func dummy() *all {
   209  	ae := &checkpoint.AssetEntry{
   210  		Id: "ETH",
   211  		AssetDetails: &vega.AssetDetails{
   212  			Name:     "ETH",
   213  			Symbol:   "ETH",
   214  			Decimals: 5,
   215  			Quantum:  "",
   216  			Source: &vega.AssetDetails_BuiltinAsset{
   217  				BuiltinAsset: &vega.BuiltinAsset{
   218  					MaxFaucetAmountMint: "100000000000",
   219  				},
   220  			},
   221  		},
   222  	}
   223  	bal := &checkpoint.AssetBalance{
   224  		Party:   "deadbeef007",
   225  		Asset:   "ETH",
   226  		Balance: "1000000",
   227  	}
   228  	prop := &vega.Proposal{
   229  		Id:        "prop-1",
   230  		Reference: "dummy-proposal",
   231  		PartyId:   "deadbeef007",
   232  		State:     vega.Proposal_STATE_ENACTED,
   233  		Timestamp: time.Now().Add(-1 * time.Hour).Unix(),
   234  		Terms: &vega.ProposalTerms{
   235  			ClosingTimestamp:    time.Now().Add(24 * time.Hour).Unix(),
   236  			EnactmentTimestamp:  time.Now().Add(-10 * time.Minute).Unix(),
   237  			ValidationTimestamp: time.Now().Add(-1*time.Hour - time.Second).Unix(),
   238  			Change: &vega.ProposalTerms_NewMarket{
   239  				NewMarket: &vega.NewMarket{
   240  					Changes: &vega.NewMarketConfiguration{
   241  						Instrument: &vega.InstrumentConfiguration{
   242  							Name: "ETH/FOO",
   243  							Code: "bar",
   244  							Product: &vega.InstrumentConfiguration_Future{
   245  								Future: &vega.FutureProduct{ // omitted oracle spec for now
   246  									SettlementAsset: "ETH",
   247  									QuoteName:       "ETH",
   248  								},
   249  							},
   250  						},
   251  						DecimalPlaces: 5,
   252  						PriceMonitoringParameters: &vega.PriceMonitoringParameters{
   253  							Triggers: []*vega.PriceMonitoringTrigger{
   254  								{
   255  									Horizon:          10,
   256  									Probability:      "0.95",
   257  									AuctionExtension: 10,
   258  								},
   259  							},
   260  						},
   261  						LiquidityMonitoringParameters: &vega.LiquidityMonitoringParameters{
   262  							TargetStakeParameters: &vega.TargetStakeParameters{
   263  								TimeWindow:    10,
   264  								ScalingFactor: 0.7,
   265  							},
   266  						},
   267  						RiskParameters: &vega.NewMarketConfiguration_LogNormal{
   268  							LogNormal: &vega.LogNormalRiskModel{
   269  								RiskAversionParameter: 0.1,
   270  								Tau:                   0.2,
   271  								Params: &vega.LogNormalModelParams{
   272  									Mu:    0.3,
   273  									R:     0.3,
   274  									Sigma: 0.3,
   275  								},
   276  							},
   277  						},
   278  						LiquiditySlaParameters: &vega.LiquiditySLAParameters{
   279  							PriceRange:                  "0.95",
   280  							CommitmentMinTimeFraction:   "0.5",
   281  							PerformanceHysteresisEpochs: 4,
   282  							SlaCompetitionFactor:        "0.5",
   283  						},
   284  					},
   285  				},
   286  			},
   287  		},
   288  	}
   289  	del := &checkpoint.Delegate{
   290  		Active: []*checkpoint.DelegateEntry{
   291  			{
   292  				Party:    "deadbeef007",
   293  				Node:     "node0",
   294  				Amount:   "100",
   295  				EpochSeq: 0,
   296  			},
   297  		},
   298  		Pending: []*checkpoint.DelegateEntry{
   299  			{
   300  				Party:      "deadbeef007",
   301  				Node:       "node0",
   302  				Amount:     "100",
   303  				Undelegate: true,
   304  				EpochSeq:   1,
   305  			},
   306  		},
   307  		AutoDelegation: []string{
   308  			"deadbeef007",
   309  		},
   310  	}
   311  	t := time.Now()
   312  	return &all{
   313  		messages: map[string]proto.Message{
   314  			"assets": &checkpoint.Assets{
   315  				Assets: []*checkpoint.AssetEntry{ae},
   316  			},
   317  			"collateral": &checkpoint.Collateral{
   318  				Balances: []*checkpoint.AssetBalance{bal},
   319  			},
   320  			"governance": &checkpoint.Proposals{
   321  				Proposals: []*vega.Proposal{prop},
   322  			},
   323  			"network_parameters": &checkpoint.NetParams{
   324  				Params: []*vega.NetworkParameter{
   325  					{
   326  						Key:   "foo",
   327  						Value: "bar",
   328  					},
   329  				},
   330  			},
   331  			"delegation": del,
   332  			"epoch": &events.EpochEvent{
   333  				Seq:        0,
   334  				Action:     vega.EpochAction_EPOCH_ACTION_START,
   335  				StartTime:  t.UnixNano(),
   336  				ExpireTime: t.Add(24 * time.Hour).UnixNano(),
   337  				EndTime:    t.Add(25 * time.Hour).UnixNano(),
   338  			},
   339  			"block": &checkpoint.Block{
   340  				Height: 1,
   341  			},
   342  			"banking": &checkpoint.Banking{
   343  				RecurringTransfers: &checkpoint.RecurringTransfers{
   344  					RecurringTransfers: []*events.Transfer{
   345  						{
   346  							Id:              "someid",
   347  							From:            "somefrom",
   348  							FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL,
   349  							To:              "someto",
   350  							ToAccountType:   vega.AccountType_ACCOUNT_TYPE_GENERAL,
   351  							Asset:           "someasset",
   352  							Amount:          "100",
   353  							Reference:       "someref",
   354  							Status:          events.Transfer_STATUS_PENDING,
   355  							Kind: &events.Transfer_Recurring{
   356  								Recurring: &events.RecurringTransfer{
   357  									StartEpoch: 10,
   358  									EndEpoch:   func() *uint64 { e := uint64(100); return &e }(),
   359  									Factor:     "1",
   360  								},
   361  							},
   362  						},
   363  					},
   364  				},
   365  			},
   366  		},
   367  	}
   368  }