github.com/InjectiveLabs/sdk-go@v1.53.0/chain/peggy/types/types.go (about)

     1  package types
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"math"
     7  	"math/big"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"cosmossdk.io/errors"
    13  	sdkmath "cosmossdk.io/math"
    14  	sdk "github.com/cosmos/cosmos-sdk/types"
    15  	"github.com/ethereum/go-ethereum/accounts/abi"
    16  	gethcommon "github.com/ethereum/go-ethereum/common"
    17  	"github.com/ethereum/go-ethereum/crypto"
    18  )
    19  
    20  // UInt64FromBytes create uint from binary big endian representation
    21  func UInt64FromBytes(s []byte) uint64 {
    22  	return binary.BigEndian.Uint64(s)
    23  }
    24  
    25  // UInt64Bytes uses the SDK byte marshaling to encode a uint64
    26  func UInt64Bytes(n uint64) []byte {
    27  	return sdk.Uint64ToBigEndian(n)
    28  }
    29  
    30  // UInt64FromString to parse out a uint64 for a nonce
    31  func UInt64FromString(s string) (uint64, error) {
    32  	return strconv.ParseUint(s, 10, 64)
    33  }
    34  
    35  //////////////////////////////////////
    36  //      BRIDGE VALIDATOR(S)         //
    37  //////////////////////////////////////
    38  
    39  // ValidateBasic performs stateless checks on validity
    40  func (b *BridgeValidator) ValidateBasic() error {
    41  	if b.Power == 0 {
    42  		return errors.Wrap(ErrEmpty, "power")
    43  	}
    44  	if err := ValidateEthAddress(b.EthereumAddress); err != nil {
    45  		return errors.Wrap(err, "ethereum address")
    46  	}
    47  	if b.EthereumAddress == "" {
    48  		return errors.Wrap(ErrEmpty, "address")
    49  	}
    50  	return nil
    51  }
    52  
    53  // BridgeValidators is the sorted set of validator data for Ethereum bridge MultiSig set
    54  type BridgeValidators []*BridgeValidator
    55  
    56  // Sort sorts the validators by power
    57  func (b BridgeValidators) Sort() {
    58  	sort.SliceStable(b, func(i, j int) bool {
    59  		if b[i].Power == b[j].Power {
    60  			// Secondary sort on eth address in case powers are equal
    61  			return EthAddrLessThan(b[i].EthereumAddress, b[j].EthereumAddress)
    62  		}
    63  		return b[i].Power > b[j].Power
    64  	})
    65  }
    66  
    67  // PowerDiff returns the difference in power between two bridge validator sets
    68  // note this is Gravity bridge power *not* Cosmos voting power. Cosmos voting
    69  // power is based on the absolute number of tokens in the staking pool at any given
    70  // time Gravity bridge power is normalized using the equation.
    71  //
    72  // validators cosmos voting power / total cosmos voting power in this block = gravity bridge power / u32_max
    73  //
    74  // As an example if someone has 52% of the Cosmos voting power when a validator set is created their Gravity
    75  // bridge voting power is u32_max * .52
    76  //
    77  // Normalized voting power dramatically reduces how often we have to produce new validator set updates. For example
    78  // if the total on chain voting power increases by 1% due to inflation, we shouldn't have to generate a new validator
    79  // set, after all the validators retained their relative percentages during inflation and normalized Gravity bridge power
    80  // shows no difference.
    81  func (b BridgeValidators) PowerDiff(c BridgeValidators) float64 {
    82  	powers := map[string]int64{}
    83  	// loop over b and initialize the map with their powers
    84  	for _, bv := range b {
    85  		powers[bv.EthereumAddress] = int64(bv.Power)
    86  	}
    87  
    88  	// subtract c powers from powers in the map, initializing
    89  	// uninitialized keys with negative numbers
    90  	for _, bv := range c {
    91  		if val, ok := powers[bv.EthereumAddress]; ok {
    92  			powers[bv.EthereumAddress] = val - int64(bv.Power)
    93  		} else {
    94  			powers[bv.EthereumAddress] = -int64(bv.Power)
    95  		}
    96  	}
    97  
    98  	var delta float64
    99  	for _, v := range powers {
   100  		// NOTE: we care about the absolute value of the changes
   101  		delta += math.Abs(float64(v))
   102  	}
   103  
   104  	return math.Abs(delta / float64(math.MaxUint32))
   105  }
   106  
   107  // TotalPower returns the total power in the bridge validator set
   108  func (b BridgeValidators) TotalPower() (out uint64) {
   109  	for _, v := range b {
   110  		out += v.Power
   111  	}
   112  	return
   113  }
   114  
   115  // HasDuplicates returns true if there are duplicates in the set
   116  func (b BridgeValidators) HasDuplicates() bool {
   117  	m := make(map[string]struct{}, len(b))
   118  	for i := range b {
   119  		m[b[i].EthereumAddress] = struct{}{}
   120  	}
   121  	return len(m) != len(b)
   122  }
   123  
   124  // GetPowers returns only the power values for all members
   125  func (b BridgeValidators) GetPowers() []uint64 {
   126  	r := make([]uint64, len(b))
   127  	for i := range b {
   128  		r[i] = b[i].Power
   129  	}
   130  	return r
   131  }
   132  
   133  // ValidateBasic performs stateless checks
   134  func (b BridgeValidators) ValidateBasic() error {
   135  	if len(b) == 0 {
   136  		return ErrEmpty
   137  	}
   138  	for i := range b {
   139  		if err := b[i].ValidateBasic(); err != nil {
   140  			return errors.Wrapf(err, "member %d", i)
   141  		}
   142  	}
   143  	if b.HasDuplicates() {
   144  		return errors.Wrap(ErrDuplicate, "addresses")
   145  	}
   146  	return nil
   147  }
   148  
   149  // NewValset returns a new valset
   150  func NewValset(nonce, height uint64, members BridgeValidators, rewardAmount sdkmath.Int, rewardToken gethcommon.Address) *Valset {
   151  	members.Sort()
   152  	mem := make([]*BridgeValidator, 0)
   153  	for _, val := range members {
   154  		mem = append(mem, val)
   155  	}
   156  	return &Valset{Nonce: nonce, Members: mem, Height: height, RewardAmount: rewardAmount, RewardToken: rewardToken.Hex()}
   157  }
   158  
   159  // GetCheckpoint returns the checkpoint hash
   160  func (v Valset) GetCheckpoint(peggyIDstring string) gethcommon.Hash {
   161  	// error case here should not occur outside of testing since the above is a constant
   162  	contractAbi, abiErr := abi.JSON(strings.NewReader(ValsetCheckpointABIJSON))
   163  	if abiErr != nil {
   164  		panic("Bad ABI constant!")
   165  	}
   166  
   167  	// the contract argument is not a arbitrary length array but a fixed length 32 byte
   168  	// array, therefore we have to utf8 encode the string (the default in this case) and
   169  	// then copy the variable length encoded data into a fixed length array. This function
   170  	// will panic if peggyId is too long to fit in 32 bytes
   171  	peggyID, err := strToFixByteArray(peggyIDstring)
   172  	if err != nil {
   173  		panic(err)
   174  	}
   175  	// this should never happen, unless an invalid paramater value has been set by the chain
   176  	err = ValidateEthAddress(v.RewardToken)
   177  	if err != nil {
   178  		panic(err)
   179  	}
   180  	rewardToken := gethcommon.HexToAddress(v.RewardToken)
   181  
   182  	if v.RewardAmount.BigInt() == nil {
   183  		// this must be programmer error
   184  		panic("Invalid reward amount passed in valset GetCheckpoint!")
   185  	}
   186  	rewardAmount := v.RewardAmount.BigInt()
   187  
   188  	checkpointBytes := []uint8("checkpoint")
   189  	var checkpoint [32]uint8
   190  	copy(checkpoint[:], checkpointBytes)
   191  
   192  	memberAddresses := make([]gethcommon.Address, len(v.Members))
   193  	convertedPowers := make([]*big.Int, len(v.Members))
   194  	for i, m := range v.Members {
   195  		memberAddresses[i] = gethcommon.HexToAddress(m.EthereumAddress)
   196  		convertedPowers[i] = big.NewInt(int64(m.Power))
   197  	}
   198  	// the word 'checkpoint' needs to be the same as the 'name' above in the checkpointAbiJson
   199  	// but other than that it's a constant that has no impact on the output. This is because
   200  	// it gets encoded as a function name which we must then discard.
   201  	bytes, packErr := contractAbi.Pack("checkpoint", peggyID, checkpoint, big.NewInt(int64(v.Nonce)), memberAddresses, convertedPowers, rewardAmount, rewardToken)
   202  	// this should never happen outside of test since any case that could crash on encoding
   203  	// should be filtered above.
   204  	if packErr != nil {
   205  		panic(fmt.Sprintf("Error packing checkpoint! %s/n", packErr))
   206  	}
   207  
   208  	// we hash the resulting encoded bytes discarding the first 4 bytes these 4 bytes are the constant
   209  	// method name 'checkpoint'. If you where to replace the checkpoint constant in this code you would
   210  	// then need to adjust how many bytes you truncate off the front to get the output of abi.encode()
   211  	hash := crypto.Keccak256Hash(bytes[4:])
   212  	return hash
   213  }
   214  
   215  // This interface is implemented by all the types that are used
   216  // to create transactions on Ethereum that are signed by validators.
   217  // The naming here could be improved.
   218  type EthereumSigned interface {
   219  	GetCheckpoint(peggyIDstring string) gethcommon.Hash
   220  }
   221  
   222  // WithoutEmptyMembers returns a new Valset without member that have 0 power or an empty Ethereum address.
   223  func (v *Valset) WithoutEmptyMembers() *Valset {
   224  	if v == nil {
   225  		return nil
   226  	}
   227  	r := Valset{Nonce: v.Nonce, Members: make([]*BridgeValidator, 0, len(v.Members))}
   228  	for i := range v.Members {
   229  		if err := v.Members[i].ValidateBasic(); err == nil {
   230  			r.Members = append(r.Members, v.Members[i])
   231  		}
   232  	}
   233  	return &r
   234  }
   235  
   236  // Valsets is a collection of valset
   237  type Valsets []*Valset
   238  
   239  func (v Valsets) Len() int {
   240  	return len(v)
   241  }
   242  
   243  func (v Valsets) Less(i, j int) bool {
   244  	return v[i].Nonce > v[j].Nonce
   245  }
   246  
   247  func (v Valsets) Swap(i, j int) {
   248  	v[i], v[j] = v[j], v[i]
   249  }
   250  
   251  // GetFees returns the total fees contained within a given batch
   252  func (b OutgoingTxBatch) GetFees() sdkmath.Int {
   253  	sum := sdkmath.ZeroInt()
   254  	for _, t := range b.Transactions {
   255  		sum.Add(t.Erc20Fee.Amount)
   256  	}
   257  	return sum
   258  }