From b97140252d98530694840031623cd86f38757350 Mon Sep 17 00:00:00 2001 From: Pouya Date: Tue, 12 Mar 2024 17:53:04 +0100 Subject: [PATCH] Fix consensus issues --- src/address/address.go | 2 +- src/config/config.go | 5 +- src/constants/constants.go | 2 +- src/plugins/logs/logs.go | 39 ++++++-------- src/plugins/uniswap/uniswap.go | 50 ++++++++--------- src/pos/pos.go | 98 +++++++++++++++++++++++++--------- src/utils/debounce.go | 8 +-- 7 files changed, 121 insertions(+), 83 deletions(-) diff --git a/src/address/address.go b/src/address/address.go index fc5d8660..8a9d0889 100644 --- a/src/address/address.go +++ b/src/address/address.go @@ -6,7 +6,7 @@ import ( "github.com/KenshiTech/unchained/crypto/shake" ) -var chars = "0123456789ABCDEFGHJKMNPQTSTUVXYZ" +var chars = "0123456789ABCDEFGHJKMNPQRSTUVXYZ" func ToBase32(input []byte) string { var output []byte diff --git a/src/config/config.go b/src/config/config.go index ab42aaa1..c69fa5b0 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -11,11 +11,12 @@ func defaults() { Config.SetDefault("name", petname.Generate(3, "-")) Config.SetDefault("log", "info") Config.SetDefault("rpc.ethereum", "https://ethereum.publicnode.com") + Config.SetDefault("rpc.arbitrum_sepolia", "https://sepolia-rollup.arbitrum.io/rpc") Config.SetDefault("broker.bind", "0.0.0.0:9123") Config.SetDefault("pos.chain", "arbitrum_sepolia") - Config.SetDefault("pos.address", "0x08cE842914b7313E16DBA708ccF0418bE3dE05c6") + Config.SetDefault("pos.address", "0xdA36f22C0Dab89CA0884489Bf3a00c0C27cEDbec") Config.SetDefault("pos.base", "1") - Config.SetDefault("pos.nft", "25000000000000000000000") + Config.SetDefault("pos.nft", "40000000000000000000000") } var Config *viper.Viper diff --git a/src/constants/constants.go b/src/constants/constants.go index e7484761..27ecaf3e 100644 --- a/src/constants/constants.go +++ b/src/constants/constants.go @@ -1,4 +1,4 @@ package constants -var Version = "0.11.18" +var Version = "0.11.19" var ProtocolVersion = "0.11.17" diff --git a/src/plugins/logs/logs.go b/src/plugins/logs/logs.go index 418f9176..9d9b7791 100644 --- a/src/plugins/logs/logs.go +++ b/src/plugins/logs/logs.go @@ -10,6 +10,7 @@ import ( "sync" "time" + "github.com/KenshiTech/unchained/address" "github.com/KenshiTech/unchained/config" "github.com/KenshiTech/unchained/constants/opcodes" "github.com/KenshiTech/unchained/crypto/bls" @@ -111,13 +112,14 @@ func RecordSignature( return } - if !historical { + // TODO: Standalone mode shouldn't call this or check consensus + blockNumber, err := GetBlockNumber(info.Chain) - blockNumber, err := GetBlockNumber(info.Chain) + if err != nil { + panic(err) + } - if err != nil { - panic(err) - } + if !historical { // TODO: this won't work for Arbitrum // TODO: we disallow syncing historical events here @@ -143,29 +145,22 @@ func RecordSignature( consensus.Add(key, make(map[bls12381.G1Affine]big.Int)) } - // TODO: This will lead to too many PoS requests - userStake, err := pos.GetVotingPowerOfPublicKey(signer.PublicKey) - - var votingPower *big.Int - var totalVoted *big.Int - - base := big.NewInt(0) - base.SetString(config.Config.GetString("pos.base"), 10) - - nft := big.NewInt(0) - nft.SetString(config.Config.GetString("pos.nft"), 10) + votingPower, err := pos.GetVotingPowerOfPublicKey( + signer.PublicKey, + big.NewInt(int64(*blockNumber)), + ) if err != nil { - votingPower = base - } else { - tokenPower := new(big.Int).Add(userStake.Amount, base) - nftPower := new(big.Int).Mul(nft, big.NewInt(int64(len(userStake.NftIds)))) - votingPower = new(big.Int).Add(tokenPower, nftPower) + log.Logger. + With("Address", address.Calculate(signer.PublicKey[:])). + With("Error", err). + Error("Failed to get voting power") + return } reportedValues, _ := consensus.Get(key) voted := reportedValues[hash] - totalVoted = new(big.Int).Add(votingPower, &voted) + totalVoted := new(big.Int).Add(votingPower, &voted) isMajority := true for _, reportCount := range reportedValues { diff --git a/src/plugins/uniswap/uniswap.go b/src/plugins/uniswap/uniswap.go index 20f791e5..19df01d1 100644 --- a/src/plugins/uniswap/uniswap.go +++ b/src/plugins/uniswap/uniswap.go @@ -88,19 +88,20 @@ func RecordSignature( return } - if !historical { - blockNumber, err := GetBlockNumber(info.Asset.Token.Chain) + // TODO: Standalone mode shouldn't call this or check consensus + blockNumber, err := GetBlockNumber(info.Asset.Token.Chain) - if err != nil { - log.Logger. - With("Network", info.Asset.Token.Chain). - With("Error", err). - Error("Failed to get the latest block number") - ethereum.RefreshRPC(info.Asset.Token.Chain) - // TODO: we should retry - return - } + if err != nil { + log.Logger. + With("Network", info.Asset.Token.Chain). + With("Error", err). + Error("Failed to get the latest block number") + ethereum.RefreshRPC(info.Asset.Token.Chain) + // TODO: we should retry + return + } + if !historical { // TODO: this won't work for Arbitrum if *blockNumber-info.Asset.Block > 96 { log.Logger. @@ -126,27 +127,20 @@ func RecordSignature( voted = *big.NewInt(0) } - // TODO: This will lead to too many PoS requests - userStake, err := pos.GetVotingPowerOfPublicKey(signer.PublicKey) - - var votingPower *big.Int - var totalVoted *big.Int - - base := big.NewInt(0) - base.SetString(config.Config.GetString("pos.base"), 10) - - nft := big.NewInt(0) - nft.SetString(config.Config.GetString("pos.nft"), 10) + votingPower, err := pos.GetVotingPowerOfPublicKey( + signer.PublicKey, + big.NewInt(int64(*blockNumber)), + ) if err != nil { - votingPower = base - } else { - tokenPower := new(big.Int).Add(userStake.Amount, base) - nftPower := new(big.Int).Mul(nft, big.NewInt(int64(len(userStake.NftIds)))) - votingPower = new(big.Int).Add(tokenPower, nftPower) + log.Logger. + With("Address", address.Calculate(signer.PublicKey[:])). + With("Error", err). + Error("Failed to get voting power") + return } - totalVoted = new(big.Int).Add(votingPower, &voted) + totalVoted := new(big.Int).Add(votingPower, &voted) reportedValues.Range(func(_ bls12381.G1Affine, value big.Int) bool { if value.Cmp(totalVoted) == 1 { diff --git a/src/pos/pos.go b/src/pos/pos.go index 24f86436..85a9a195 100644 --- a/src/pos/pos.go +++ b/src/pos/pos.go @@ -13,20 +13,77 @@ import ( ) var posContract *contracts.UnchainedStaking +var votingPowers map[[20]byte]big.Int +var stakes map[[20]byte]contracts.UnchainedStakingStake func GetTotalVotingPower() (*big.Int, error) { return posContract.TotalVotingPower(nil) } -func GetVotingPower(address [20]byte) (contracts.UnchainedStakingStake, error) { +func GetStake(address [20]byte, block *big.Int) (contracts.UnchainedStakingStake, error) { + cached, ok := stakes[address] + + if ok && cached.Unlock.Cmp(block) >= 0 { + return cached, nil + } + stake, err := posContract.StakeOf0(nil, address) + + if err == nil { + if stake.Amount.Cmp(big.NewInt(0)) == 0 { + // TODO: we should listen to stake changed events + // TODO: and update stakes accordingly + stake.Unlock = new(big.Int).Add(block, big.NewInt(25000)) + } + + stakes[address] = stake + } + return stake, err } -func GetVotingPowerOfPublicKey(pkBytes [96]byte) (contracts.UnchainedStakingStake, error) { +func GetVotingPower(address [20]byte, block *big.Int) (*big.Int, error) { + + if votingPower, ok := votingPowers[address]; ok { + return &votingPower, nil + } + + stake, err := GetStake(address, block) + + if err != nil { + return nil, err + } + + base := big.NewInt(0) + base.SetString(config.Config.GetString("pos.base"), 10) + + nft := big.NewInt(0) + nft.SetString(config.Config.GetString("pos.nft"), 10) + + nftPower := new(big.Int).Mul(nft, big.NewInt(int64(len(stake.NftIds)))) + votingPower := new(big.Int).Add(stake.Amount, nftPower) + + if votingPower.Cmp(base) < 0 { + votingPower = base + } + + votingPowers[address] = *votingPower + return votingPower, nil +} + +func GetVotingPowerOfPublicKey( + pkBytes [96]byte, + block *big.Int, +) (*big.Int, error) { _, addrHex := address.CalculateHex(pkBytes[:]) - stake, err := posContract.StakeOf0(nil, addrHex) - return stake, err + return GetVotingPower(addrHex, block) +} + +func VotingPowerToFloat(power *big.Int) *big.Float { + decimalPlaces := big.NewInt(1e18) + powerFloat := new(big.Float).SetInt(power) + powerFloat.Quo(powerFloat, new(big.Float).SetInt(decimalPlaces)) + return powerFloat } func Start() { @@ -35,8 +92,8 @@ func Start() { addrHexStr, addrHex := address.CalculateHex(pkBytes[:]) log.Logger. - With("Link", addrHexStr). - Info("Unchained EVM") + With("Hex", addrHexStr). + Info("Unchained") var err error @@ -54,27 +111,16 @@ func Start() { os.Exit(1) } - stake, _ := GetVotingPower(addrHex) + power, _ := GetVotingPower(addrHex, big.NewInt(0)) total, _ := GetTotalVotingPower() - var votingPower *big.Int - - base := big.NewInt(0) - base.SetString(config.Config.GetString("pos.base"), 10) - - nft := big.NewInt(0) - nft.SetString(config.Config.GetString("pos.nft"), 10) - - if err != nil { - votingPower = base - } else { - tokenPower := new(big.Int).Add(stake.Amount, base) - nftPower := new(big.Int).Mul(nft, big.NewInt(int64(len(stake.NftIds)))) - votingPower = new(big.Int).Add(tokenPower, nftPower) - } - log.Logger. - With("Power", votingPower.String()). - With("Network", total.String()). - Info("Voting power") + With("Power", VotingPowerToFloat(power)). + With("Network", VotingPowerToFloat(total)). + Info("PoS") +} + +func init() { + votingPowers = make(map[[20]byte]big.Int) + stakes = make(map[[20]byte]contracts.UnchainedStakingStake) } diff --git a/src/utils/debounce.go b/src/utils/debounce.go index d20322c5..8f4fea56 100644 --- a/src/utils/debounce.go +++ b/src/utils/debounce.go @@ -31,10 +31,12 @@ func Debounce[KeyType comparable, ArgType any]( context.timers[key] = time.AfterFunc(wait, func() { context.Lock() - defer context.Unlock() - delete(context.timers, key) - function(arg) + + go func() { + defer context.Unlock() + function(arg) + }() }) } }