From b710862003e1699797e20ac806d0d4b6369ef8e1 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 23 Jan 2024 17:57:15 +0100 Subject: [PATCH 01/39] Added prioritised handling of submitter contract --- coreth/core/daemon.go | 29 +++++++++++++++++++++++++++++ coreth/core/state_transition.go | 23 +++++++++++++---------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index 90bd8cc7..d6652c73 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -80,6 +80,26 @@ func GetPrioritisedFTSOContract(blockTime *big.Int) string { } } +func GetPrioritisedSubmitterContract(blockTime *big.Int) string { + switch { + default: + return "0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89" + } +} + +func IsPrioritisedContractCall(to *common.Address, ret []byte, blockTime *big.Int) bool { + switch { + case to == nil: + return false + case *to == common.HexToAddress(GetPrioritisedFTSOContract(blockTime)): + return true + case *to == common.HexToAddress(GetPrioritisedSubmitterContract(blockTime)): + return !isZeroSlice(ret) + default: + return false + } +} + func GetMaximumMintRequest(blockTime *big.Int) *big.Int { switch { default: @@ -157,3 +177,12 @@ func atomicDaemonAndMint(evm EVMCaller, log log.Logger) { log.Warn("Daemon error", "error", daemonErr) } } + +func isZeroSlice(s []byte) bool { + for i := len(s) - 1; i >= 0; i-- { + if s[i] != 0 { + return false + } + } + return true +} diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index d6a49bb2..337fc450 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -56,8 +56,10 @@ The state transitioning model does all the necessary work to work out a valid ne 3) Create a new state object if the recipient is \0*32 4) Value transfer == If contract creation == - 4a) Attempt to run transaction data - 4b) If valid, use result as code for the new state object + + 4a) Attempt to run transaction data + 4b) If valid, use result as code for the new state object + == end == 5) Run Script section 6) Derive new state root @@ -300,13 +302,13 @@ func (st *StateTransition) preCheck() error { // TransitionDb will transition the state by applying the current message and // returning the evm execution result with following fields. // -// - used gas: -// total gas used (including gas being refunded) -// - returndata: -// the returned data from evm -// - concrete execution error: -// various **EVM** error which aborts the execution, -// e.g. ErrOutOfGas, ErrExecutionReverted +// - used gas: +// total gas used (including gas being refunded) +// - returndata: +// the returned data from evm +// - concrete execution error: +// various **EVM** error which aborts the execution, +// e.g. ErrOutOfGas, ErrExecutionReverted // // However if any consensus issue encountered, return the error directly with // nil evm execution result. @@ -417,7 +419,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } st.refundGas(rules.IsApricotPhase1) - if vmerr == nil && msg.To() != nil && *msg.To() == common.HexToAddress(GetPrioritisedFTSOContract(timestamp)) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { + // if vmerr == nil && msg.To() != nil && *msg.To() == common.HexToAddress(GetPrioritisedFTSOContract(timestamp)) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { + if vmerr == nil && IsPrioritisedContractCall(msg.To(), ret, timestamp) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { nominalGasUsed := uint64(params.TxGas) // 21000 nominalGasPrice := uint64(params.ApricotPhase4MinBaseFee) // 25_000_000_000; the max base fee is 1_000_000_000_000 nominalFee := new(big.Int).Mul(new(big.Int).SetUint64(nominalGasUsed), new(big.Int).SetUint64(nominalGasPrice)) From 1048112b09f213e8ec0d611cfad82419fd9f81cb Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Thu, 25 Jan 2024 00:17:54 +0100 Subject: [PATCH 02/39] Fork times for submitter contract --- coreth/core/daemon.go | 40 ++++++++++++++++++++++++++------- coreth/core/daemon_test.go | 30 +++++++++++++++++++++++++ coreth/core/state_transition.go | 3 +-- 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index d6652c73..8a8c2376 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -6,11 +6,27 @@ package core import ( "fmt" "math/big" + "os" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ava-labs/coreth/core/vm" + "github.com/ava-labs/coreth/params" +) + +var ( + // Define activation times for submitter contract + submitterContractActivationTimeFlare = big.NewInt(time.Date(2024, time.March, 1, 12, 0, 0, 0, time.UTC).Unix()) + submitterContractActivationTimeCostwo = big.NewInt(time.Date(2024, time.January, 31, 12, 0, 0, 0, time.UTC).Unix()) + + // Define ftso and submitter contract addresses + prioritisedFTSOContractAddress = common.HexToAddress("0x1000000000000000000000000000000000000003") + + prioritisedSubmitterContractAddressFlare = common.HexToAddress("0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89") + prioritisedSubmitterContractAddressCostwo = common.HexToAddress("0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89") + prioritisedSubmitterContractAddressEnv = common.HexToAddress(os.Getenv("SUBMITTER_CONTRACT_ADDRESS")) // for local and staging chains ) // Define errors @@ -73,27 +89,35 @@ func GetDaemonSelector(blockTime *big.Int) []byte { } } -func GetPrioritisedFTSOContract(blockTime *big.Int) string { +func isPrioritisedFTSOContract(to *common.Address, blockTime *big.Int) bool { switch { default: - return "0x1000000000000000000000000000000000000003" + return *to == prioritisedFTSOContractAddress } } -func GetPrioritisedSubmitterContract(blockTime *big.Int) string { +func isPrioritisedSubmitterContract(chainID *big.Int, to *common.Address, blockTime *big.Int) bool { switch { + case chainID.Cmp(params.FlareChainID) == 0: + return *to == prioritisedSubmitterContractAddressFlare && + blockTime.Cmp(submitterContractActivationTimeFlare) >= 0 + case chainID.Cmp(params.CostwoChainID) == 0: + return *to == prioritisedSubmitterContractAddressCostwo && + blockTime.Cmp(submitterContractActivationTimeCostwo) >= 0 + case chainID.Cmp(params.LocalFlareChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0: + return *to == prioritisedSubmitterContractAddressEnv default: - return "0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89" + return false } } -func IsPrioritisedContractCall(to *common.Address, ret []byte, blockTime *big.Int) bool { +func IsPrioritisedContractCall(chainID *big.Int, to *common.Address, ret []byte, blockTime *big.Int) bool { switch { - case to == nil: + case to == nil || chainID == nil || blockTime == nil: return false - case *to == common.HexToAddress(GetPrioritisedFTSOContract(blockTime)): + case isPrioritisedFTSOContract(to, blockTime): return true - case *to == common.HexToAddress(GetPrioritisedSubmitterContract(blockTime)): + case isPrioritisedSubmitterContract(chainID, to, blockTime): return !isZeroSlice(ret) default: return false diff --git a/coreth/core/daemon_test.go b/coreth/core/daemon_test.go index 88d224da..8d316780 100644 --- a/coreth/core/daemon_test.go +++ b/coreth/core/daemon_test.go @@ -7,11 +7,13 @@ import ( "errors" "math/big" "testing" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ava-labs/coreth/core/vm" + "github.com/ava-labs/coreth/params" ) // Define a mock structure to spy and mock values for daemon calls @@ -476,3 +478,31 @@ func TestDaemonShouldNotMintMoreThanLimit(t *testing.T) { t.Errorf("Add balance call count not as expected. got %d want 1", defaultEVMMock.mockEVMCallerData.addBalanceCalls) } } + +func TestPrioritisedContract(t *testing.T) { + address := common.HexToAddress("0x123456789aBCdEF123456789aBCdef123456789A") + preForkTime := big.NewInt(time.Date(2024, time.February, 1, 12, 0, 0, 0, time.UTC).Unix()) + postForkTime := big.NewInt(time.Date(2024, time.March, 2, 12, 0, 0, 0, time.UTC).Unix()) + ret0 := [32]byte{} + ret1 := [32]byte{} + ret1[31] = 1 + + if IsPrioritisedContractCall(params.FlareChainID, &address, nil, preForkTime) { + t.Errorf("Expected false for wrong address") + } + if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedFTSOContractAddress, nil, preForkTime) { + t.Errorf("Expected true for FTSO contract") + } + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, ret1[:], preForkTime) { + t.Errorf("Expected false for submitter contract before activation") + } + if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, ret1[:], postForkTime) { + t.Errorf("Expected true for submitter contract after activation") + } + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, ret0[:], postForkTime) { + t.Errorf("Expected false for submitter contract with wrong return value") + } + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, nil, postForkTime) { + t.Errorf("Expected false for submitter contract with no return value") + } +} diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index 337fc450..f5f5f9b4 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -419,8 +419,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } st.refundGas(rules.IsApricotPhase1) - // if vmerr == nil && msg.To() != nil && *msg.To() == common.HexToAddress(GetPrioritisedFTSOContract(timestamp)) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { - if vmerr == nil && IsPrioritisedContractCall(msg.To(), ret, timestamp) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { + if vmerr == nil && IsPrioritisedContractCall(chainID, msg.To(), ret, timestamp) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { nominalGasUsed := uint64(params.TxGas) // 21000 nominalGasPrice := uint64(params.ApricotPhase4MinBaseFee) // 25_000_000_000; the max base fee is 1_000_000_000_000 nominalFee := new(big.Int).Mul(new(big.Int).SetUint64(nominalGasUsed), new(big.Int).SetUint64(nominalGasPrice)) From d6d641d6ea3f848face4b04da0ec1a4a7f6cda6e Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Thu, 25 Jan 2024 11:23:56 +0100 Subject: [PATCH 03/39] Simplified function for checking ftso prioritised contract --- coreth/core/daemon.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index 8a8c2376..17d9d5d0 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -24,8 +24,8 @@ var ( // Define ftso and submitter contract addresses prioritisedFTSOContractAddress = common.HexToAddress("0x1000000000000000000000000000000000000003") - prioritisedSubmitterContractAddressFlare = common.HexToAddress("0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89") - prioritisedSubmitterContractAddressCostwo = common.HexToAddress("0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89") + prioritisedSubmitterContractAddressFlare = common.HexToAddress("0x200000000000000000000000000000000000000a") + prioritisedSubmitterContractAddressCostwo = common.HexToAddress("0x300000000000000000000000000000000000000b") prioritisedSubmitterContractAddressEnv = common.HexToAddress(os.Getenv("SUBMITTER_CONTRACT_ADDRESS")) // for local and staging chains ) @@ -89,15 +89,14 @@ func GetDaemonSelector(blockTime *big.Int) []byte { } } -func isPrioritisedFTSOContract(to *common.Address, blockTime *big.Int) bool { - switch { - default: - return *to == prioritisedFTSOContractAddress - } +func isPrioritisedFTSOContract(to *common.Address) bool { + return to != nil && *to == prioritisedFTSOContractAddress } func isPrioritisedSubmitterContract(chainID *big.Int, to *common.Address, blockTime *big.Int) bool { switch { + case to == nil || chainID == nil || blockTime == nil: + return false case chainID.Cmp(params.FlareChainID) == 0: return *to == prioritisedSubmitterContractAddressFlare && blockTime.Cmp(submitterContractActivationTimeFlare) >= 0 @@ -113,9 +112,7 @@ func isPrioritisedSubmitterContract(chainID *big.Int, to *common.Address, blockT func IsPrioritisedContractCall(chainID *big.Int, to *common.Address, ret []byte, blockTime *big.Int) bool { switch { - case to == nil || chainID == nil || blockTime == nil: - return false - case isPrioritisedFTSOContract(to, blockTime): + case isPrioritisedFTSOContract(to): return true case isPrioritisedSubmitterContract(chainID, to, blockTime): return !isZeroSlice(ret) From 18a7aeede908ab216db9677e138e0fc825aba633 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 12 Feb 2024 16:58:10 +0100 Subject: [PATCH 04/39] Change of default attestors addresses --- coreth/core/daemon.go | 4 ++-- coreth/core/state_connector.go | 34 ++++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index 17d9d5d0..0bb4a06b 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -99,10 +99,10 @@ func isPrioritisedSubmitterContract(chainID *big.Int, to *common.Address, blockT return false case chainID.Cmp(params.FlareChainID) == 0: return *to == prioritisedSubmitterContractAddressFlare && - blockTime.Cmp(submitterContractActivationTimeFlare) >= 0 + blockTime.Cmp(submitterContractActivationTimeFlare) > 0 case chainID.Cmp(params.CostwoChainID) == 0: return *to == prioritisedSubmitterContractAddressCostwo && - blockTime.Cmp(submitterContractActivationTimeCostwo) >= 0 + blockTime.Cmp(submitterContractActivationTimeCostwo) > 0 case chainID.Cmp(params.LocalFlareChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0: return *to == prioritisedSubmitterContractAddressEnv default: diff --git a/coreth/core/state_connector.go b/coreth/core/state_connector.go index 06993612..23196169 100644 --- a/coreth/core/state_connector.go +++ b/coreth/core/state_connector.go @@ -115,16 +115,30 @@ func FinaliseRoundSelector(chainID *big.Int, blockTime *big.Int) []byte { func GetDefaultAttestors(chainID *big.Int, blockTime *big.Int) []common.Address { switch { case chainID.Cmp(params.FlareChainID) == 0: - return []common.Address{ - common.HexToAddress("0x0988Cf4828F4e4eD0cE7c07467E70e19095Ee152"), - common.HexToAddress("0x6BC7DCa62010D418eB72CCdc58561e00C5868Ef1"), - common.HexToAddress("0xE34Bb361536610a9DCcEa5292262e36AfF65c06c"), - common.HexToAddress("0x8A3D627D86A81F5D21683F4963565C63DB5e1309"), - common.HexToAddress("0x2D3e7e4b19bDc920fd9C57BD3072A31F5a59FeC8"), - common.HexToAddress("0x6455dC38fdF739b6fE021b30C7D9672C1c6DEb5c"), - common.HexToAddress("0x49893c5Dfc035F4eE4E46faC014f6D4bC80F7f92"), - common.HexToAddress("0x08e8b2Af4874e920de27723576A13d66008Af523"), - common.HexToAddress("0x5D2f75392DdDa69a2818021dd6a64937904c8352"), + if blockTime.Cmp(submitterContractActivationTimeFlare) > 0 { + return []common.Address{ + common.HexToAddress("0x0988Cf4828F4e4eD0cE7c07467E70e19095Ee152"), + common.HexToAddress("0x6BC7DCa62010D418eB72CCdc58561e00C5868Ef1"), + common.HexToAddress("0xE34Bb361536610a9DCcEa5292262e36AfF65c06c"), + common.HexToAddress("0x8A3D627D86A81F5D21683F4963565C63DB5e1309"), + common.HexToAddress("0x2D3e7e4b19bDc920fd9C57BD3072A31F5a59FeC8"), + common.HexToAddress("0x6455dC38fdF739b6fE021b30C7D9672C1c6DEb5c"), + common.HexToAddress("0x49893c5Dfc035F4eE4E46faC014f6D4bC80F7f92"), + common.HexToAddress("0x08e8b2Af4874e920de27723576A13d66008Af523"), + common.HexToAddress("0x5D2f75392DdDa69a2818021dd6a64937904c8352"), + } + } else { + return []common.Address{ + common.HexToAddress("0x0988Cf4828F4e4eD0cE7c07467E70e19095Ee152"), + common.HexToAddress("0x6BC7DCa62010D418eB72CCdc58561e00C5868Ef1"), + common.HexToAddress("0xE34Bb361536610a9DCcEa5292262e36AfF65c06c"), + common.HexToAddress("0x8A3D627D86A81F5D21683F4963565C63DB5e1309"), + common.HexToAddress("0x2D3e7e4b19bDc920fd9C57BD3072A31F5a59FeC8"), + common.HexToAddress("0x6455dC38fdF739b6fE021b30C7D9672C1c6DEb5c"), + common.HexToAddress("0x49893c5Dfc035F4eE4E46faC014f6D4bC80F7f92"), + common.HexToAddress("0x08e8b2Af4874e920de27723576A13d66008Af523"), + common.HexToAddress("0x5D2f75392DdDa69a2818021dd6a64937904c8352"), + } } case chainID.Cmp(params.SongbirdChainID) == 0: return []common.Address{ From f15d6d2cfacc87aca874707fa9fe6c953a7f4a43 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 20 Feb 2024 14:17:47 +0100 Subject: [PATCH 05/39] Updated submitter fork addresses --- coreth/core/daemon.go | 13 ++++++------- coreth/core/daemon_test.go | 8 ++++---- coreth/core/state_connector.go | 18 +++++++++--------- coreth/core/state_transition.go | 2 +- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index 0bb4a06b..f6f416e9 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -18,15 +18,14 @@ import ( var ( // Define activation times for submitter contract - submitterContractActivationTimeFlare = big.NewInt(time.Date(2024, time.March, 1, 12, 0, 0, 0, time.UTC).Unix()) - submitterContractActivationTimeCostwo = big.NewInt(time.Date(2024, time.January, 31, 12, 0, 0, 0, time.UTC).Unix()) + submitterContractActivationTimeFlare = big.NewInt(time.Date(2024, time.March, 26, 12, 0, 0, 0, time.UTC).Unix()) + submitterContractActivationTimeCostwo = big.NewInt(time.Date(2024, time.February, 21, 12, 0, 0, 0, time.UTC).Unix()) // Define ftso and submitter contract addresses prioritisedFTSOContractAddress = common.HexToAddress("0x1000000000000000000000000000000000000003") - prioritisedSubmitterContractAddressFlare = common.HexToAddress("0x200000000000000000000000000000000000000a") - prioritisedSubmitterContractAddressCostwo = common.HexToAddress("0x300000000000000000000000000000000000000b") - prioritisedSubmitterContractAddressEnv = common.HexToAddress(os.Getenv("SUBMITTER_CONTRACT_ADDRESS")) // for local and staging chains + prioritisedSubmitterContractAddress = common.HexToAddress("0x2cA6571Daa15ce734Bbd0Bf27D5C9D16787fc33f") + prioritisedSubmitterContractAddressEnv = common.HexToAddress(os.Getenv("SUBMITTER_CONTRACT_ADDRESS")) // for local and staging chains ) // Define errors @@ -98,10 +97,10 @@ func isPrioritisedSubmitterContract(chainID *big.Int, to *common.Address, blockT case to == nil || chainID == nil || blockTime == nil: return false case chainID.Cmp(params.FlareChainID) == 0: - return *to == prioritisedSubmitterContractAddressFlare && + return *to == prioritisedSubmitterContractAddress && blockTime.Cmp(submitterContractActivationTimeFlare) > 0 case chainID.Cmp(params.CostwoChainID) == 0: - return *to == prioritisedSubmitterContractAddressCostwo && + return *to == prioritisedSubmitterContractAddress && blockTime.Cmp(submitterContractActivationTimeCostwo) > 0 case chainID.Cmp(params.LocalFlareChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0: return *to == prioritisedSubmitterContractAddressEnv diff --git a/coreth/core/daemon_test.go b/coreth/core/daemon_test.go index 8d316780..dafafcd3 100644 --- a/coreth/core/daemon_test.go +++ b/coreth/core/daemon_test.go @@ -493,16 +493,16 @@ func TestPrioritisedContract(t *testing.T) { if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedFTSOContractAddress, nil, preForkTime) { t.Errorf("Expected true for FTSO contract") } - if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, ret1[:], preForkTime) { + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret1[:], preForkTime) { t.Errorf("Expected false for submitter contract before activation") } - if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, ret1[:], postForkTime) { + if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret1[:], postForkTime) { t.Errorf("Expected true for submitter contract after activation") } - if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, ret0[:], postForkTime) { + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret0[:], postForkTime) { t.Errorf("Expected false for submitter contract with wrong return value") } - if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddressFlare, nil, postForkTime) { + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, nil, postForkTime) { t.Errorf("Expected false for submitter contract with no return value") } } diff --git a/coreth/core/state_connector.go b/coreth/core/state_connector.go index 23196169..2792735b 100644 --- a/coreth/core/state_connector.go +++ b/coreth/core/state_connector.go @@ -117,15 +117,15 @@ func GetDefaultAttestors(chainID *big.Int, blockTime *big.Int) []common.Address case chainID.Cmp(params.FlareChainID) == 0: if blockTime.Cmp(submitterContractActivationTimeFlare) > 0 { return []common.Address{ - common.HexToAddress("0x0988Cf4828F4e4eD0cE7c07467E70e19095Ee152"), - common.HexToAddress("0x6BC7DCa62010D418eB72CCdc58561e00C5868Ef1"), - common.HexToAddress("0xE34Bb361536610a9DCcEa5292262e36AfF65c06c"), - common.HexToAddress("0x8A3D627D86A81F5D21683F4963565C63DB5e1309"), - common.HexToAddress("0x2D3e7e4b19bDc920fd9C57BD3072A31F5a59FeC8"), - common.HexToAddress("0x6455dC38fdF739b6fE021b30C7D9672C1c6DEb5c"), - common.HexToAddress("0x49893c5Dfc035F4eE4E46faC014f6D4bC80F7f92"), - common.HexToAddress("0x08e8b2Af4874e920de27723576A13d66008Af523"), - common.HexToAddress("0x5D2f75392DdDa69a2818021dd6a64937904c8352"), + common.HexToAddress("0x4E07E1F3DB3Dc9BAd56Cc829747cc0148234329F"), + common.HexToAddress("0xB264Fad6Fdc65767998f93501945aB8F9108809d"), + common.HexToAddress("0x366BeC54195bfD45DBB34b79Ad2dEC4010598947"), + common.HexToAddress("0x2665B179d5fCE1118f06e23B5d6E7617c5Ff733A"), + common.HexToAddress("0x65cBaFaDD7C914179aabcE9C35f918a4E36AfFf9"), + common.HexToAddress("0x7eC6a7C7c4Ef003A75DC6c06352B48B37Ac2191B"), + common.HexToAddress("0xEa9bC2F98eFFC6A27E2C31733c1905961826f73B"), + common.HexToAddress("0xA4aA75a9B49c7f2B4be62b2999d7103E78D004C7"), + common.HexToAddress("0x4DF8436D7578C2d3bc73d33B6644913e131B70FC"), } } else { return []common.Address{ diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index f5f5f9b4..77d815d3 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -420,7 +420,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.refundGas(rules.IsApricotPhase1) if vmerr == nil && IsPrioritisedContractCall(chainID, msg.To(), ret, timestamp) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { - nominalGasUsed := uint64(params.TxGas) // 21000 + nominalGasUsed := params.TxGas // 21000 nominalGasPrice := uint64(params.ApricotPhase4MinBaseFee) // 25_000_000_000; the max base fee is 1_000_000_000_000 nominalFee := new(big.Int).Mul(new(big.Int).SetUint64(nominalGasUsed), new(big.Int).SetUint64(nominalGasPrice)) actualGasUsed := st.gasUsed() From b55e073e46915e2b9dd9a9a72046a3d087cea9ca Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 20 Feb 2024 14:28:15 +0100 Subject: [PATCH 06/39] Updated submitter fork test date --- coreth/core/daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index f6f416e9..e69a799d 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -19,7 +19,7 @@ import ( var ( // Define activation times for submitter contract submitterContractActivationTimeFlare = big.NewInt(time.Date(2024, time.March, 26, 12, 0, 0, 0, time.UTC).Unix()) - submitterContractActivationTimeCostwo = big.NewInt(time.Date(2024, time.February, 21, 12, 0, 0, 0, time.UTC).Unix()) + submitterContractActivationTimeCostwo = big.NewInt(time.Date(2024, time.February, 21, 14, 0, 0, 0, time.UTC).Unix()) // Define ftso and submitter contract addresses prioritisedFTSOContractAddress = common.HexToAddress("0x1000000000000000000000000000000000000003") From 6f85d4bafc17eca44aa63e4d31d3b00dbc881977 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 20 Feb 2024 14:53:56 +0100 Subject: [PATCH 07/39] Fixed failing test --- coreth/core/daemon_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreth/core/daemon_test.go b/coreth/core/daemon_test.go index dafafcd3..7be96a2f 100644 --- a/coreth/core/daemon_test.go +++ b/coreth/core/daemon_test.go @@ -481,8 +481,8 @@ func TestDaemonShouldNotMintMoreThanLimit(t *testing.T) { func TestPrioritisedContract(t *testing.T) { address := common.HexToAddress("0x123456789aBCdEF123456789aBCdef123456789A") - preForkTime := big.NewInt(time.Date(2024, time.February, 1, 12, 0, 0, 0, time.UTC).Unix()) - postForkTime := big.NewInt(time.Date(2024, time.March, 2, 12, 0, 0, 0, time.UTC).Unix()) + preForkTime := big.NewInt(time.Date(2024, time.March, 20, 12, 0, 0, 0, time.UTC).Unix()) + postForkTime := big.NewInt(time.Date(2024, time.March, 27, 12, 0, 0, 0, time.UTC).Unix()) ret0 := [32]byte{} ret1 := [32]byte{} ret1[31] = 1 From a986263d8375e2146832b12acc896cd3b0b8abfb Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 21 Feb 2024 12:20:14 +0100 Subject: [PATCH 08/39] Updated patch version --- avalanchego/version/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avalanchego/version/constants.go b/avalanchego/version/constants.go index ab2809fd..57654a75 100644 --- a/avalanchego/version/constants.go +++ b/avalanchego/version/constants.go @@ -14,7 +14,7 @@ var ( Current = &Semantic{ Major: 1, Minor: 7, - Patch: 1806, + Patch: 1807, } CurrentApp = &Application{ Major: Current.Major, From 83d56e0838a5453fab04071d3386853368183703 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 4 Mar 2024 11:13:19 +0100 Subject: [PATCH 09/39] Songbird support modifications --- avalanchego/genesis/config.go | 38 +++++- avalanchego/genesis/genesis.go | 22 +-- avalanchego/genesis/genesis_coston.go | 106 +++++++++++++++ avalanchego/genesis/genesis_local.go | 108 +++++++++------ avalanchego/genesis/genesis_songbird.go | 128 ++++++++++++++++++ avalanchego/genesis/genesis_test.go | 6 +- avalanchego/genesis/params.go | 18 ++- avalanchego/node/node.go | 4 +- ...dd034a2fd67b932f10e3dba1d2bbd39348695.json | 1 + ...deID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.crt | 27 ++++ ...deID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.key | 52 +++++++ ...deID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.crt | 27 ++++ ...deID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.key | 52 +++++++ ...deID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.crt | 27 ++++ ...deID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.key | 52 +++++++ ...deID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.crt | 27 ++++ ...deID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.key | 52 +++++++ ...deID-HaZ4HpanjndqSuN252chFsTysmdND5meA.crt | 27 ++++ ...deID-HaZ4HpanjndqSuN252chFsTysmdND5meA.key | 52 +++++++ avalanchego/snow/validators/custom.go | 35 +++++ avalanchego/snow/validators/manager.go | 14 +- avalanchego/utils/constants/network_ids.go | 26 +++- .../txs/executor/inflation_settings.go | 11 ++ .../txs/executor/proposal_tx_executor.go | 4 +- coreth/consensus/dummy/consensus.go | 32 +++-- coreth/core/chain_makers.go | 11 +- coreth/miner/worker.go | 14 +- coreth/params/avalanche_params.go | 3 + coreth/params/config.go | 13 ++ coreth/plugin/evm/block_verification.go | 25 +++- 30 files changed, 918 insertions(+), 96 deletions(-) create mode 100644 avalanchego/genesis/genesis_coston.go create mode 100644 avalanchego/genesis/genesis_songbird.go create mode 100644 avalanchego/scripts/keys/6b0dd034a2fd67b932f10e3dba1d2bbd39348695.json create mode 100644 avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.crt create mode 100644 avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.key create mode 100644 avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.crt create mode 100644 avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.key create mode 100644 avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.crt create mode 100644 avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.key create mode 100644 avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.crt create mode 100644 avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.key create mode 100644 avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.crt create mode 100644 avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.key create mode 100644 avalanchego/snow/validators/custom.go diff --git a/avalanchego/genesis/config.go b/avalanchego/genesis/config.go index 36512861..fda76ae1 100644 --- a/avalanchego/genesis/config.go +++ b/avalanchego/genesis/config.go @@ -124,6 +124,12 @@ func (c Config) Unparse() (UnparsedConfig, error) { } func (c *Config) InitialSupply() (uint64, error) { + // SGB-MERGE + // For songbird, coston and local networks, the initial supply is 1 + if c.NetworkID == constants.SongbirdID || c.NetworkID == constants.CostonID || c.NetworkID == constants.LocalID { + return 1, nil + } + initialSupply := uint64(0) for _, allocation := range c.Allocations { newInitialSupply, err := safemath.Add64(initialSupply, allocation.InitialAmount) @@ -169,6 +175,14 @@ var ( // LocalFlareConfig is the config that should be used to generate a localFlare // genesis. LocalFlareConfig Config + + // SongbirdConfig is the config that should be used to generate the Songbird + // canary network genesis. + SongbirdConfig Config + + // CostonConfig is the config tat should be used to generate the Coston test + // network genesis. + CostonConfig Config ) func init() { @@ -179,16 +193,20 @@ func init() { unparsedCostwoConfig := UnparsedConfig{} unparsedStagingConfig := UnparsedConfig{} unparsedLocalFlareConfig := UnparsedConfig{} + unparsedSongbirdConfig := UnparsedConfig{} + unparsedCostonConfig := UnparsedConfig{} errs := wrappers.Errs{} errs.Add( json.Unmarshal(mainnetGenesisConfigJSON, &unparsedMainnetConfig), json.Unmarshal(fujiGenesisConfigJSON, &unparsedFujiConfig), - json.Unmarshal(localGenesisConfigJSON, &unparsedLocalConfig), + json.Unmarshal([]byte(localGenesisConfigJSON), &unparsedLocalConfig), json.Unmarshal(flareGenesisConfigJSON, &unparsedFlareConfig), json.Unmarshal(costwoGenesisConfigJSON, &unparsedCostwoConfig), json.Unmarshal(stagingGenesisConfigJSON, &unparsedStagingConfig), json.Unmarshal(localFlareGenesisConfigJSON, &unparsedLocalFlareConfig), + json.Unmarshal([]byte(songbirdGenesisConfigJSON), &unparsedSongbirdConfig), + json.Unmarshal([]byte(costonGenesisConfigJSON), &unparsedCostonConfig), ) if errs.Errored() { panic(errs.Err) @@ -203,6 +221,7 @@ func init() { FujiConfig = fujiConfig localConfig, err := unparsedLocalConfig.Parse() + localConfig.CChainGenesis = localCChainGenesis errs.Add(err) LocalConfig = localConfig @@ -222,6 +241,14 @@ func init() { errs.Add(err) LocalFlareConfig = localFlareConfig + songbirdConfig, err := unparsedSongbirdConfig.Parse() + errs.Add(err) + SongbirdConfig = songbirdConfig + + costonConfig, err := unparsedCostonConfig.Parse() + errs.Add(err) + CostonConfig = costonConfig + if errs.Errored() { panic(errs.Err) } @@ -231,8 +258,9 @@ func GetConfig(networkID uint32) *Config { switch networkID { case constants.MainnetID: return &MainnetConfig - case constants.FujiID: - return &FujiConfig + // SGB-MERGE + // case constants.FujiID: + // return &FujiConfig case constants.LocalID: return &LocalConfig case constants.FlareID: @@ -243,6 +271,10 @@ func GetConfig(networkID uint32) *Config { return &StagingConfig case constants.LocalFlareID: return &LocalFlareConfig + case constants.SongbirdID: + return &SongbirdConfig + case constants.CostonID: + return &CostonConfig default: tempConfig := LocalConfig tempConfig.NetworkID = networkID diff --git a/avalanchego/genesis/genesis.go b/avalanchego/genesis/genesis.go index 2e5c935f..44958eca 100644 --- a/avalanchego/genesis/genesis.go +++ b/avalanchego/genesis/genesis.go @@ -180,9 +180,9 @@ func validateConfig(networkID uint32, config *Config) error { // loads the network genesis data from the config at [filepath]. // // FromFile returns: -// 1) The byte representation of the genesis state of the platform chain -// (ie the genesis state of the network) -// 2) The asset ID of AVAX +// 1. The byte representation of the genesis state of the platform chain +// (ie the genesis state of the network) +// 2. The asset ID of AVAX func FromFile(networkID uint32, filepath string) ([]byte, ids.ID, error) { switch networkID { case constants.MainnetID, constants.TestnetID, constants.LocalID: @@ -221,9 +221,9 @@ func FromFile(networkID uint32, filepath string) ([]byte, ids.ID, error) { // loads the network genesis data from [genesisContent]. // // FromFlag returns: -// 1) The byte representation of the genesis state of the platform chain -// (ie the genesis state of the network) -// 2) The asset ID of AVAX +// 1. The byte representation of the genesis state of the platform chain +// (ie the genesis state of the network) +// 2. The asset ID of AVAX func FromFlag(networkID uint32, genesisContent string) ([]byte, ids.ID, error) { switch networkID { case constants.MainnetID, constants.TestnetID, constants.LocalID: @@ -247,9 +247,9 @@ func FromFlag(networkID uint32, genesisContent string) ([]byte, ids.ID, error) { } // FromConfig returns: -// 1) The byte representation of the genesis state of the platform chain -// (ie the genesis state of the network) -// 2) The asset ID of AVAX +// 1. The byte representation of the genesis state of the platform chain +// (ie the genesis state of the network) +// 2. The asset ID of AVAX func FromConfig(config *Config) ([]byte, ids.ID, error) { hrp := constants.GetHRP(config.NetworkID) @@ -456,6 +456,10 @@ func FromConfig(config *Config) ([]byte, ids.ID, error) { } func splitAllocations(allocations []Allocation, numSplits int) [][]Allocation { + if numSplits == 0 { + return nil + } + totalAmount := uint64(0) for _, allocation := range allocations { for _, unlock := range allocation.UnlockSchedule { diff --git a/avalanchego/genesis/genesis_coston.go b/avalanchego/genesis/genesis_coston.go new file mode 100644 index 00000000..7618e39a --- /dev/null +++ b/avalanchego/genesis/genesis_coston.go @@ -0,0 +1,106 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package genesis + +import ( + "time" + + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" +) + +var ( + costonGenesisConfigJSON = `{ + "networkID": 7, + "allocations": [], + "startTime": 1631778950, + "initialStakeDuration": 31536000, + "initialStakeDurationOffset": 0, + "initialStakedFunds": [], + "initialStakers": [], + "cChainGenesis": "", + "message": "flare" + }` + + // costonChainGenesis is the C-Chain genesis block used for the Coston + // test network. + costonCChainGenesis = `{ + "config": { + "chainId": 16, + "homesteadBlock": 0, + "daoForkBlock": 0, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "apricotPhase1BlockTimestamp": 0, + "apricotPhase2BlockTimestamp": 0 + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x00", + "gasLimit": "0x5f5e100", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0100000000000000000000000000000000000000", + "alloc": { + "1000000000000000000000000000000000000001": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101735760003560e01c80637ec93e9f116100de578063c5d64cd111610097578063dccb2d3211610071578063dccb2d32146107f2578063ee2d87371461081e578063ef4c169e1461083b578063f9c490ee1461084357610173565b8063c5d64cd11461073e578063d3b92926146107b4578063d3fb3e9f146107d157610173565b80637ec93e9f146105a85780637f582432146105d45780638abd90ae1461069c5780638b203dd4146106a45780639c531f8c1461072e578063adc890321461073657610173565b806347453f371161013057806347453f37146104355780634bdc9c8f1461045257806355d14c15146104d857806371e8d61a14610504578063741d3cb41461057457806379fd4e1a1461057c57610173565b806307003bb41461017857806322ce7387146101945780632a2434a2146101cc5780632bec6f8714610247578063388492dd1461028f5780633f57987d14610403575b600080fd5b61018061084b565b604080519115158252519081900360200190f35b6101ba600480360360208110156101aa57600080fd5b50356001600160a01b0316610854565b60408051918252519081900360200190f35b6101ef600480360360208110156101e257600080fd5b503563ffffffff16610866565b60405180876001600160401b03168152602001866001600160401b031681526020018561ffff168152602001846001600160401b03168152602001838152602001828152602001965050505050505060405180910390f35b6102736004803603604081101561025d57600080fd5b506001600160a01b038135169060200135610a98565b604080516001600160401b039092168252519081900360200190f35b610357600480360360808110156102a557600080fd5b63ffffffff823516916020810135916001600160401b0360408301351691908101906080810160608201356401000000008111156102e257600080fd5b8201836020820111156102f457600080fd5b8035906020019184600183028401116401000000008311171561031657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610abe945050505050565b604051808663ffffffff168152602001856001600160401b03168152602001846001600160401b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b838110156103c45781810151838201526020016103ac565b50505050905090810190601f1680156103f15780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b6101806004803603604081101561041957600080fd5b50803563ffffffff1690602001356001600160401b03166114cb565b6102736004803603602081101561044b57600080fd5b50356115c7565b61046f6004803603602081101561046857600080fd5b5035611625565b604080519a15158b5260208b0199909952898901979097526060890195909552608088019390935260a08701919091526001600160401b0390811660c08701521660e085015215156101008401526001600160a01b031661012083015251908190036101400190f35b61046f600480360360408110156104ee57600080fd5b50803590602001356001600160401b031661169a565b610548600480360360a081101561051a57600080fd5b5063ffffffff813516906020810135906040810135906001600160401b036060820135169060800135611718565b604080516001600160401b03948516815292909316602083015215158183015290519081900360600190f35b6101ba61194b565b61046f6004803603604081101561059257600080fd5b50803590602001356001600160401b0316611951565b61046f600480360360408110156105be57600080fd5b506001600160a01b0381351690602001356119cf565b610357600480360360808110156105ea57600080fd5b63ffffffff823516916020810135916001600160401b03604083013516919081019060808101606082013564010000000081111561062757600080fd5b82018360208201111561063957600080fd5b8035906020019184600183028401116401000000008311171561065b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611a4c945050505050565b6101ba612460565b6106c7600480360360208110156106ba57600080fd5b503563ffffffff16612466565b604080519a15158b526001600160401b03998a1660208c01529789168a89015261ffff96871660608b015294909516608089015291861660a088015290941660c086015260e085019390935261010084015261012083019190915251908190036101400190f35b6102736124d6565b6101ba6124e5565b61077c6004803603608081101561075457600080fd5b5063ffffffff813516906001600160401b036020820135169060408101359060600135612588565b6040805163ffffffff90951685526001600160401b03909316602085015261ffff909116838301526060830152519081900360800190f35b610273600480360360208110156107ca57600080fd5b503561375d565b6107d9613778565b6040805163ffffffff9092168252519081900360200190f35b6102736004803603604081101561080857600080fd5b506001600160a01b038135169060200135613789565b61046f6004803603602081101561083457600080fd5b50356137fd565b610180613870565b6101ba614280565b60005460ff1681565b600d6020526000908152604090205481565b600080548190819081908190819060ff166108b25760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8716600090815260056020526040902054879060ff1661090c576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b600560008963ffffffff1663ffffffff168152602001908152602001600020600201549250600560008963ffffffff1663ffffffff168152602001908152602001600020600401549150600088600560008b63ffffffff1663ffffffff16815260200190815260200160002060000160159054906101000a90046001600160401b0316604051602001808363ffffffff1660e01b8152600401826001600160401b031660c01b81526008019250505060405160208183030381529060405280519060200120905060066000336001600160a01b03166001600160a01b03168152602001908152602001600020600082815260200190815260200160002060000160009054906101000a900460ff1615610a435733600090815260066020908152604080832084845290915281206004015490945092505b505063ffffffff969096166000908152600560205260409020805460019091015461010082046001600160401b0390811699600160a81b8404821699600160881b90940461ffff169850911695509193509150565b600b6020908152600092835260408084209091529082529020546001600160401b031681565b6000805481908190819060609060ff16610b095760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8916600090815260056020526040902054899060ff16610b63576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b88610baa576040805162461bcd60e51b815260206004820152601260248201527107061796d656e7448617368203d3d203078360741b604482015290519081900360640190fd5b63ffffffff8a166000908152600560205260409020600101546001600160401b0316610c075760405162461bcd60e51b81526004018080602001828103825260298152602001806144706029913960400191505060405180910390fd5b41331480610c18575041600160981b145b610c69576040805162461bcd60e51b815260206004820152601c60248201527f696e76616c696420626c6f636b2e636f696e626173652076616c756500000000604482015290519081900360640190fd5b6040805160e08c901b6001600160e01b03191660208083019190915260248083018d905283518084039091018152604490920183528151918101919091206000818152600a90925291902060060154600160801b900460ff1615610d0d576040805162461bcd60e51b81526020600482015260166024820152753830bcb6b2b73a1030b63932b0b23c90383937bb32b760511b604482015290519081900360640190fd5b63ffffffff8b166000908152600560205260409020600101546001600160401b03908116908a1610610d705760405162461bcd60e51b815260040180806020018281038252602e815260200180614442602e913960400191505060405180910390fd5b63ffffffff8b166000908152600560205260409020546001600160401b036101008204811691600160481b90041615610e515763ffffffff8c16600090815260056020526040902080546001909101546001600160401b03600160481b830481166101009093048116918116919091031611610e1d5760405162461bcd60e51b815260040180806020018281038252603981526020018061457e6039913960400191505060405180910390fd5b5063ffffffff8b16600090815260056020526040902080546001909101546001600160401b03600160481b90920482169116035b806001600160401b03168a6001600160401b03161015610eb8576040805162461bcd60e51b815260206004820152601a60248201527f6c6564676572203c20696e646578536561726368526567696f6e000000000000604482015290519081900360640190fd5b60008281526008602090815260408083206001600160401b038e16845290915281205460ff16156110415760008381526008602090815260408083206001600160401b038f168452909152902060040154421015610f475760405162461bcd60e51b81526004018080602001828103825260498152602001806143656049913960600191505060405180910390fd5b60008381526008602090815260408083206001600160401b038f1684529091529020600501548c14610fb6576040805162461bcd60e51b81526020600482015260136024820152720d2dcecc2d8d2c840e0c2f2dacadce890c2e6d606b1b604482015290519081900360640190fd5b426003546008600086815260200190815260200160002060008e6001600160401b03166001600160401b0316815260200190815260200160002060030154011161103c576040805162461bcd60e51b815260206004820152601260248201527172657665616c20697320746f6f206c61746560701b604482015290519081900360640190fd5b61107d565b413314801590611054575041600160981b145b1561107d575063ffffffff8c166000908152600560205260409020600101546001600160401b03165b4133148015611090575041600160981b14155b156114ba5760008381526008602090815260408083206001600160401b038f16845290915290205460ff16611269576040518061014001604052806001151581526020016000801b81526020016000801b8152602001428152602001600254420181526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600015158152602001336001600160a01b03168152506008600085815260200190815260200160002060008d6001600160401b03166001600160401b0316815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506114ba565b6040518061014001604052806001151581526020016000801b81526020016000801b8152602001600081526020014281526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600115158152602001336001600160a01b0316815250600a600085815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050507ff794f5b9b6791b3ab0eb97342cf2efbb7e710a478d4512a2505ea60c93fd85d38d8c8c8f33604051808663ffffffff168152602001856001600160401b0316815260200180602001848152602001836001600160a01b03168152602001828103825285818151815260200191508051906020019080838360005b8381101561147b578181015183820152602001611463565b50505050905090810190601f1680156114a85780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a15b9b9c999b9a99975050505050505050565b6000805460ff1661150d5760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8316600090815260056020526040902054839060ff16611567576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b50506040805160e09390931b6001600160e01b03191660208085019190915260c09290921b6001600160c01b03191660248401528051808403600c018152602c909301815282519282019290922060009081526007909152205460ff1690565b6000805460ff166116095760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b506000908152600c60205260409020546001600160401b031690565b60076020819052600091825260409091208054600182015460028301546003840154600485015460058601546006870154969097015460ff95861697949693959294919391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b60086020908152600092835260408084209091529082529020805460018201546002830154600384015460048501546005860154600687015460079097015460ff96871697959694959394929391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b600080548190819060ff1661175e5760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8816600090815260056020526040902054889060ff166117b8576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b604080516001600160401b0388166020808301919091528251808303820181528284018452805190820120606083018c9052608083018b905260a083015260c08083018990528351808403909101815260e08084018552815191830191909120908d901b6001600160e01b0319166101008401526101048084018290528451808503909101815261012490930184528251928201929092206000818152600a9092529290205490919060ff166118ae576040805162461bcd60e51b81526020600482015260166024820152751c185e5b595b9d08191bd95cc81b9bdd08195e1a5cdd60521b604482015290519081900360640190fd5b6000818152600a60205260409020600501548214611909576040805162461bcd60e51b81526020600482015260136024820152720d2dcecc2d8d2c840e0c2f2dacadce890c2e6d606b1b604482015290519081900360640190fd5b6000908152600a60205260409020600601546001600160401b038082169c600160401b83049091169b50600160801b90910460ff169950975050505050505050565b60025481565b60096020908152600092835260408084209091529082529020805460018201546002830154600384015460048501546005860154600687015460079097015460ff96871697959694959394929391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b600660208181526000938452604080852090915291835291208054600182015460028301546003840154600485015460058601549686015460079096015460ff95861697949693959294919391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b6000805481908190819060609060ff16611a975760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8916600090815260056020526040902054899060ff16611af1576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b88611b38576040805162461bcd60e51b815260206004820152601260248201527107061796d656e7448617368203d3d203078360741b604482015290519081900360640190fd5b63ffffffff8a166000908152600560205260409020600101546001600160401b0316611b955760405162461bcd60e51b81526004018080602001828103825260298152602001806144706029913960400191505060405180910390fd5b41331480611ba6575041600160981b145b611bf7576040805162461bcd60e51b815260206004820152601c60248201527f696e76616c696420626c6f636b2e636f696e626173652076616c756500000000604482015290519081900360640190fd5b6040805160e08c901b6001600160e01b03191660208083019190915260248083018d905283518084039091018152604490920183528151918101919091206000818152600a90925291902060060154600160801b900460ff1615611c98576040805162461bcd60e51b81526020600482015260136024820152723a3c24b21030b63932b0b23c90383937bb32b760691b604482015290519081900360640190fd5b6000818152600a60205260409020600601546001600160401b03808b16911610611cf35760405162461bcd60e51b815260040180806020018281038252602f8152602001806143ae602f913960400191505060405180910390fd5b63ffffffff8b166000908152600560205260409020600101546001600160401b03908116908a1610611d565760405162461bcd60e51b815260040180806020018281038252602e815260200180614442602e913960400191505060405180910390fd5b63ffffffff8b166000908152600560205260409020546001600160401b036101008204811691600160481b90041615611e375763ffffffff8c16600090815260056020526040902080546001909101546001600160401b03600160481b830481166101009093048116918116919091031611611e035760405162461bcd60e51b815260040180806020018281038252603981526020018061457e6039913960400191505060405180910390fd5b5063ffffffff8b16600090815260056020526040902080546001909101546001600160401b03600160481b90920482169116035b806001600160401b03168a6001600160401b03161015611e9e576040805162461bcd60e51b815260206004820152601a60248201527f6c6564676572203c20696e646578536561726368526567696f6e000000000000604482015290519081900360640190fd5b60008281526009602090815260408083206001600160401b038e16845290915281205460ff16156120275760008381526009602090815260408083206001600160401b038f168452909152902060040154421015611f2d5760405162461bcd60e51b81526004018080602001828103825260548152602001806142876054913960600191505060405180910390fd5b60008381526009602090815260408083206001600160401b038f1684529091529020600501548c14611f9c576040805162461bcd60e51b81526020600482015260136024820152720d2dcecc2d8d2c840e0c2f2dacadce890c2e6d606b1b604482015290519081900360640190fd5b426003546009600086815260200190815260200160002060008e6001600160401b03166001600160401b03168152602001908152602001600020600301540111612022576040805162461bcd60e51b815260206004820152601260248201527172657665616c20697320746f6f206c61746560701b604482015290519081900360640190fd5b612063565b41331480159061203a575041600160981b145b15612063575063ffffffff8c166000908152600560205260409020600101546001600160401b03165b4133148015612076575041600160981b14155b156114ba5760008381526009602090815260408083206001600160401b038f16845290915290205460ff1661224f576040518061014001604052806001151581526020016000801b81526020016000801b8152602001428152602001600254420181526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600015158152602001336001600160a01b03168152506009600085815260200190815260200160002060008d6001600160401b03166001600160401b0316815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506114ba565b6040518061014001604052806001151581526020016000801b81526020016000801b8152602001600081526020014281526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600015158152602001336001600160a01b0316815250600a600085815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050507ff794f5b9b6791b3ab0eb97342cf2efbb7e710a478d4512a2505ea60c93fd85d38d8c8c8f33604051808663ffffffff168152602001856001600160401b0316815260200180602001848152602001836001600160a01b03168152602001828103825285818151815260200191508051906020019080838360008381101561147b578181015183820152602001611463565b60035481565b6005602052600090815260409020805460018201546002830154600384015460049094015460ff8416946001600160401b036101008604811695600160481b810482169561ffff600160881b8304811696600160981b840490911695600160a81b9093048416949190931692918a565b6004546001600160401b031681565b6000805460ff166125275760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b60015442116125675760405162461bcd60e51b81526004018080602001828103825260218152602001806145286021913960400191505060405180910390fd5b6004546001546001600160401b039091169042038161258257fe5b04905090565b6000805481908190819060ff166125d05760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8816600090815260056020526040902054889060ff1661262a576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b336000908152600d602052604090205442116126775760405162461bcd60e51b81526004018080602001828103825260458152602001806143dd6045913960600191505060405180910390fd5b866126b35760405162461bcd60e51b81526004018080602001828103825260218152602001806145d76021913960400191505060405180910390fd5b856126fb576040805162461bcd60e51b81526020600482015260136024820152720636861696e54697048617368203d3d2030783606c1b604482015290519081900360640190fd5b63ffffffff8916600090815260056020526040902054600160981b900461ffff166127575760405162461bcd60e51b81526004018080602001828103825260248152602001806144ca6024913960400191505060405180910390fd5b41331480612768575041600160981b145b6127b9576040805162461bcd60e51b815260206004820152601c60248201527f696e76616c696420626c6f636b2e636f696e626173652076616c756500000000604482015290519081900360640190fd5b63ffffffff891660009081526005602052604090208054600190910154600160881b90910461ffff166001600160401b0391821601811690891614612836576040805162461bcd60e51b815260206004820152600e60248201526d34b73b30b634b2103632b233b2b960911b604482015290519081900360640190fd5b63ffffffff8916600090815260056020526040902060020154421161288c5760405162461bcd60e51b81526004018080602001828103825260358152602001806145496035913960400191505060405180910390fd5b63ffffffff89166000908152600560205260409020600381015460049091015460020210156129215763ffffffff89166000908152600560205260409020600481015460029182015491024291909103600302101561291c5760405162461bcd60e51b815260040180806020018281038252602c815260200180614339602c913960400191505060405180910390fd5b612983565b63ffffffff8916600090815260056020526040902060048101546002909101544203600f0110156129835760405162461bcd60e51b815260040180806020018281038252602c815260200180614339602c913960400191505060405180910390fd5b63ffffffff8916600090815260056020908152604080832054815160e08e901b6001600160e01b03191681850152600160a81b90910460c01b6001600160c01b03191660248201528151808203600c018152602c9091018252805190830120808452600790925290912060060154600160801b900460ff1615612a4d576040805162461bcd60e51b815260206004820152601e60248201527f6c6f636174696f6e4861736820616c72656164792066696e616c697365640000604482015290519081900360640190fd5b63ffffffff8a16600090815260056020526040902054600160a81b90046001600160401b031615612b595760008a6001600560008e63ffffffff1663ffffffff16815260200190815260200160002060000160159054906101000a90046001600160401b031603604051602001808363ffffffff1660e01b8152600401826001600160401b031660c01b8152600801925050506040516020818303038152906040528051906020012090506007600082815260200190815260200160002060060160109054906101000a900460ff16612b575760405162461bcd60e51b81526004018080602001828103825260318152602001806144996031913960400191505060405180910390fd5b505b33600090815260066020908152604080832084845290915281205460ff1615612ce157336000908152600660209081526040808320858452909152902060040154421015612bd85760405162461bcd60e51b815260040180806020018281038252605e8152602001806142db605e913960600191505060405180910390fd5b6040805133606081901b60208084019190915260348084018d90528451808503909101815260549093018452825192810192909220600091825260068352838220868352909252919091206002015414612c70576040805162461bcd60e51b81526020600482015260146024820152730d2dcecc2d8d2c840c6d0c2d2dca8d2e090c2e6d60631b604482015290519081900360640190fd5b6003805433600090815260066020908152604080832087845290915290209091015442910111612cdc576040805162461bcd60e51b815260206004820152601260248201527172657665616c20697320746f6f206c61746560701b604482015290519081900360640190fd5b612d1c565b413314801590612cf4575041600160981b145b15612d1c575063ffffffff8a16600090815260056020526040902054600160981b900461ffff165b4133148015612d2f575041600160981b14155b156137485733600090815260066020908152604080832085845290915290205460ff16612f8b5763ffffffff8b1660009081526005602052604090206004015460028054910490811015612d8257506002545b6040518061014001604052806001151581526020018b81526020018a815260200142815260200182420181526020016000801b81526020018c6001600160401b0316815260200160006001600160401b0316815260200160001515815260200160006001600160a01b031681525060066000336001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506000805160206144228339815191528c8c600033604051808563ffffffff168152602001846001600160401b03168152602001836003811115612f6357fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a150613748565b63ffffffff8b16600090815260056020526040902054600160981b810461ffff16600160a81b9091046001600160401b0316111561335c5760008b600560008e63ffffffff1663ffffffff16815260200190815260200160002060000160139054906101000a900461ffff1661ffff16600560008f63ffffffff1663ffffffff16815260200190815260200160002060000160159054906101000a90046001600160401b031603604051602001808363ffffffff1660e01b8152600401826001600160401b031660c01b815260080192505050604051602081830303815290604052805190602001209050896007600083815260200190815260200160002060050154141561321e57600061309e6124e5565b90506001600b60006007600086815260200190815260200160002060070160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001908152602001600020600083815260200190815260200160002060008282829054906101000a90046001600160401b03160192506101000a8154816001600160401b0302191690836001600160401b031602179055506001600c600083815260200190815260200160002060008282829054906101000a90046001600160401b03160192506101000a8154816001600160401b0302191690836001600160401b031602179055506000805160206144228339815191528d8d60026007600087815260200190815260200160002060070160009054906101000a90046001600160a01b0316604051808563ffffffff168152602001846001600160401b031681526020018360038111156131f657fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a150613356565b600560008d63ffffffff1663ffffffff16815260200190815260200160002060030154600560008e63ffffffff1663ffffffff16815260200190815260200160002060000160139054906101000a900461ffff1661ffff16024201600d60006007600085815260200190815260200160002060070160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001908152602001600020819055506000805160206144228339815191528c8c60036007600086815260200190815260200160002060070160009054906101000a90046001600160a01b0316604051808563ffffffff168152602001846001600160401b0316815260200183600381111561333357fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a15b506133be565b6000805160206144228339815191528b8b600133604051808563ffffffff168152602001846001600160401b0316815260200183600381111561339b57fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a15b6040518061014001604052806001151581526020018a81526020016000801b815260200160066000336001600160a01b03166001600160a01b0316815260200190815260200160002060008581526020019081526020016000206003015481526020014281526020018981526020018b6001600160401b0316815260200160006001600160401b03168152602001600115158152602001336001600160a01b03168152506007600084815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506001600560008d63ffffffff1663ffffffff16815260200190815260200160002060000160158282829054906101000a90046001600160401b03160192506101000a8154816001600160401b0302191690836001600160401b0316021790555089600560008d63ffffffff1663ffffffff16815260200190815260200160002060010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060006002600560008e63ffffffff1663ffffffff16815260200190815260200160002060040154600560008f63ffffffff1663ffffffff1681526020019081526020016000206002015460066000336001600160a01b03166001600160a01b031681526020019081526020016000206000878152602001908152602001600020600301540301816136a757fe5b63ffffffff8e1660009081526005602052604090206003015491900491506002028111156136f75763ffffffff8c1660009081526005602052604090206003810154600202600490910155613713565b63ffffffff8c1660009081526005602052604090206004018190555b5033600090815260066020908152604080832085845282528083206003015463ffffffff8f1684526005909252909120600201555b999a6000199990990199989650505050505050565b600c602052600090815260409020546001600160401b031681565b600054610100900463ffffffff1681565b6000805460ff166137cb5760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b506001600160a01b03919091166000908152600b6020908152604080832093835292905220546001600160401b031690565b600a602052600090815260409020805460018201546002830154600384015460048501546005860154600687015460079097015460ff96871697959694959394929391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b6000805460ff16156138c0576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b604051806101400160405280600115158152602001620a84946001600160401b0316815260200160006001600160401b03168152602001600161ffff168152602001600461ffff16815260200160006001600160401b03168152602001620a84946001600160401b031681526020014281526020016103848152602001601e815250600560008063ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e0820151816002015561010082015181600301556101208201518160040155905050604051806101400160405280600115158152602001621fd4de6001600160401b0316815260200160006001600160401b03168152602001600161ffff168152602001600c61ffff16815260200160006001600160401b03168152602001621fd4de6001600160401b0316815260200142815260200160968152602001601e81525060056000600163ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e0820151816002015561010082015181600301556101208201518160040155905050604051806101400160405280600115158152602001623980b46001600160401b0316815260200160006001600160401b03168152602001600261ffff168152602001602861ffff16815260200160006001600160401b03168152602001623980b46001600160401b0316815260200142815260200160788152602001601e81525060056000600263ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160020155610100820151816003015561012082015181600401559050506040518061014001604052806001151581526020016303bf79006001600160401b0316815260200160006001600160401b03168152602001601e61ffff168152602001600161ffff16815260200160006001600160401b031681526020016303bf79006001600160401b0316815260200142815260200160788152602001601e81525060056000600363ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e082015181600201556101008201518160030155610120820151816004015590505060405180610140016040528060011515815260200162f6ba806001600160401b0316815260200160006001600160401b03168152602001601a61ffff168152602001600161ffff16815260200160006001600160401b0316815260200162f6ba806001600160401b0316815260200142815260200160788152602001601e81525060056000600463ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160020155610100820151816003015561012082015181600401559050506005600060016101000a81548163ffffffff021916908363ffffffff160217905550601e6002819055506201518060038190555062093a80600460006101000a8154816001600160401b0302191690836001600160401b031602179055504260018190555060016000806101000a81548160ff0219169083151502179055506001905090565b6001548156fe626c6f636b2e74696d657374616d70203c2070726f706f7365644e6f6e5061796d656e7450726f6f66735b6c6f636174696f6e486173685d5b6c65646765725d2e7065726d697474656452657665616c54696d65626c6f636b2e74696d657374616d70203c2070726f706f73656444617461417661696c6162696c69747950726f6f66735b6d73672e73656e6465725d5b6c6f636174696f6e486173685d2e7065726d697474656452657665616c54696d656e6f7420656e6f7567682074696d6520656c61707365642073696e6365207072696f722066696e616c697479626c6f636b2e74696d657374616d70203c2070726f706f7365645061796d656e7450726f6f66735b6c6f636174696f6e486173685d2e7065726d697474656452657665616c54696d6566696e616c697365645061796d656e74735b6c6f636174696f6e486173685d2e696e646578203e3d206c65646765726d73672e73656e6465722069732063757272656e746c792062616e6e656420666f722073656e64696e6720616e20756e616363657074656420636861696e546970486173685d63f7a167c8b58acdc7fe63a4a0972032b67a76b50a2d5fd8d64061f44952316c6564676572203e3d20636861696e735b636861696e49645d2e66696e616c697365644c6564676572496e646578636861696e735b636861696e49645d2e66696e616c697365644c6564676572496e646578203d3d203070726576696f75732064617461417661696c6162696c697479506572696f64206e6f74207965742066696e616c69736564636861696e735b636861696e49645d2e6e756d436f6e6669726d6174696f6e73203e2030737461746520636f6e6e6563746f72206973206e6f7420696e697469616c697365642c2072756e20696e697469616c697365436861696e732829626c6f636b2e74696d657374616d70203c3d20696e697469616c69736554696d65626c6f636b2e74696d657374616d70203c3d20636861696e735b636861696e49645d2e66696e616c6973656454696d657374616d7066696e616c697365644c6564676572496e646578202d2067656e657369734c6564676572203c3d206c6564676572486973746f727953697a65636861696e496420646f6573206e6f742065786973740000000000000000000064617461417661696c6162696c697479506572696f6448617368203d3d20307830a26469706673582212209aec6c293dc2b1ff65eed9d71736d7da7ca3d806bfbd209f092ee91d3612309464736f6c63430007060033" + }, + "1000000000000000000000000000000000000002": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c80638be2fb8611610104578063d38bfff4116100a2578063e9de7d6011610071578063e9de7d6014610322578063ecdda0dd14610335578063ed21b6e414610348578063ee323c921461035f576101cf565b8063d38bfff4146102e9578063d48a38df146102fc578063dded1b4714610304578063e371aef01461030c576101cf565b8063a6817ace116100de578063a6817ace146102be578063be0522e0146102c6578063c373a08e146102ce578063c9f960eb146102e1576101cf565b80638be2fb861461029b5780638ccf77a0146102a35780639d6a890f146102ab576101cf565b806362da19a511610171578063689c49991161014b578063689c49991461026557806372993615146102785780637fec8d3814610280578063870196b814610288576101cf565b806362da19a51461023c578063639031431461024457806363d4a53a1461024c576101cf565b80635042916c116101ad5780635042916c146102025780635aa6e675146102175780635d36b1901461022c57806360f7ac9714610234576101cf565b806310663750146101d45780631d76dea1146101f25780634f6a77b5146101fa575b600080fd5b6101dc610372565b6040516101e991906127a1565b60405180910390f35b6101dc610378565b6101dc61037e565b61021561021036600461249c565b610384565b005b61021f6103da565b6040516101e99190612592565b6102156103e9565b61021f6104ab565b6101dc6104ba565b6101dc6104c0565b6102546104d0565b6040516101e9959493929190612698565b61021561027336600461240d565b61050b565b6101dc61083a565b6101dc610840565b61021561029636600461249c565b6108eb565b6101dc610a3b565b610215610a41565b6102156102b93660046123f1565b610a9c565b6101dc610aa1565b61021f610aa7565b6102156102dc3660046123f1565b610ab6565b61021f610b5b565b6102156102f73660046123f1565b610b9a565b6101dc610c5c565b6101dc610c62565b610314610c68565b6040516101e992919061277f565b61021561033036600461249c565b610c89565b6102546103433660046124b4565b610dc2565b61035061111f565b6040516101e993929190612623565b61021561036d3660046123f1565b6112e4565b60055481565b60095481565b60085481565b6000546001600160a01b031633146103d5576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600c55565b6000546001600160a01b031681565b6001546001600160a01b03163314610438576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6001546001600160a01b031681565b60045481565b60006104ca6113fb565b90505b90565b600b546060908190819081906000906104fa90600160c01b90046001600160401b03166001610dc2565b945094509450945094509091929394565b6000546001600160a01b0316331461055c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b604080518082019091526008815267746f6f206d616e7960c01b60208201528190600a8211156105a85760405162461bcd60e51b815260040161059f919061276c565b60405180910390fd5b506105b1611406565b60005b818110156108345760008484838181106105ca57fe5b6105e092602060409092020190810191506123f1565b6001600160a01b031614156040518060400160405280600c81526020016b61646472657373207a65726f60a01b8152509061062e5760405162461bcd60e51b815260040161059f919061276c565b5060105460005b818110156106d7576010818154811061064a57fe5b6000918252602090912001546001600160a01b031686868581811061066b57fe5b61068192602060409092020190810191506123f1565b6001600160a01b031614156040518060400160405280600b81526020016a647570206164647265737360a81b815250906106ce5760405162461bcd60e51b815260040161059f919061276c565b50600101610635565b5060108585848181106106e657fe5b6106fc92602060409092020190810191506123f1565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905584848381811061073a57fe5b905060400201602001356011600087878681811061075457fe5b61076a92602060409092020190810191506123f1565b6001600160a01b03166001600160a01b03168152602001908152602001600020819055506000601260008787868181106107a057fe5b6107b692602060409092020190810191506123f1565b6001600160a01b031681526020810191909152604001600020557f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb598585848181106107fd57fe5b61081392602060409092020190810191506123f1565b6001604051610823929190612737565b60405180910390a1506001016105b4565b50505050565b60075481565b6002546000906001600160a01b031661088557610885306040518060400160405280600e81526020016d696e666c6174696f6e207a65726f60901b81525060006114b6565b60026001609c1b01331461089857600080fd5b6108a061167a565b905060006108ac611ee5565b90504781146108e7576108e7306040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b81525060006114b6565b5090565b6000546001600160a01b0316331461093c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60085461094c90606e6064611f0a565b811115604051806040016040528060118152602001700dac2f040dad2dce840e8dede40d0d2ced607b1b815250906109975760405162461bcd60e51b815260040161059f919061276c565b5060408051808201909152601081526f6d6178206d696e74206973207a65726f60801b6020820152816109dd5760405162461bcd60e51b815260040161059f919061276c565b5062015180600a54014211604051806040016040528060128152602001711d1a5b594819d85c081d1bdbc81cda1bdc9d60721b81525090610a315760405162461bcd60e51b815260040161059f919061276c565b5060085542600a55565b60035481565b6000546001600160a01b03163314610a92576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b610a9a611406565b565bfe5b50565b600a5481565b6002546001600160a01b031681565b6000546001600160a01b03163314610b07576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b600f5460009060ff16610b8857600f805460ff191660011790556000610b7f612012565b91506104cd9050565b506000546001600160a01b03166104cd565b6000546001600160a01b03163314610beb576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b600c5481565b60065481565b600b546001600160c01b03811690600160c01b90046001600160401b031682565b60025460408051808201909152600d81526c3737ba1034b7333630ba34b7b760991b602082015233916001600160a01b03168214610cda5760405162461bcd60e51b815260040161059f919061276c565b5060085482111560405180604001604052806007815260200166746f6f2062696760c81b81525090610d1f5760405162461bcd60e51b815260040161059f919061276c565b5042610d296113fb565b10604051806040016040528060098152602001683a37b79037b33a32b760b91b81525090610d6a5760405162461bcd60e51b815260040161059f919061276c565b508115610dbe5742600955600454610d829083612032565b6004556040517f4c4f1efc376f31abeb51b72c5f9ed81cf4016591312bb02337e58149dcfaaab490610db59084906127a1565b60405180910390a15b5050565b606080606080600060148054905087106040518060400160405280601081526020016f0e6e8c2e4e840d2dcc8caf040d0d2ced60831b81525090610e195760405162461bcd60e51b815260040161059f919061276c565b506014546000908888011115610e3457601454889003610e36565b865b9050806001600160401b0381118015610e4e57600080fd5b50604051908082528060200260200182016040528015610e78578160200160208202803683370190505b509550806001600160401b0381118015610e9157600080fd5b50604051908082528060200260200182016040528015610ebb578160200160208202803683370190505b509450806001600160401b0381118015610ed457600080fd5b50604051908082528060200260200182016040528015610f0857816020015b6060815260200190600190039081610ef35790505b509350806001600160401b0381118015610f2157600080fd5b50604051908082528060200260200182016040528015610f4b578160200160208202803683370190505b50925060005b818110156111035760006014828b0181548110610f6a57fe5b6000918252602080832090910154808352601390915260409091205489519192506001600160c01b031690899084908110610fa157fe5b6020026020010181815250506013600082815260200190815260200160002060000160189054906101000a90046001600160401b03166001600160401b0316878381518110610fec57fe5b6020908102919091018101919091526000828152601382526040908190206002908101805483516001821615610100026000190190911692909204601f810185900485028301850190935282825290929091908301828280156110905780601f1061106557610100808354040283529160200191611090565b820191906000526020600020905b81548152906001019060200180831161107357829003601f168201915b50505050508683815181106110a157fe5b60200260200101819052506013600082815260200190815260200160002060010160009054906101000a90046001600160a01b03168583815181106110e257fe5b6001600160a01b039092166020928302919091019091015250600101610f51565b5050600b549497939650919450926001600160c01b0316919050565b60105460609081908190806001600160401b038111801561113f57600080fd5b50604051908082528060200260200182016040528015611169578160200160208202803683370190505b509350806001600160401b038111801561118257600080fd5b506040519080825280602002602001820160405280156111ac578160200160208202803683370190505b509250806001600160401b03811180156111c557600080fd5b506040519080825280602002602001820160405280156111ef578160200160208202803683370190505b50915060005b818110156112dd5760006010828154811061120c57fe5b9060005260206000200160009054906101000a90046001600160a01b031690508086838151811061123957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060116000826001600160a01b03166001600160a01b031681526020019081526020016000205485838151811061128b57fe5b60200260200101818152505060126000826001600160a01b03166001600160a01b03168152602001908152602001600020548483815181106112c957fe5b6020908102919091010152506001016111f5565b5050909192565b6000546001600160a01b03163314611335576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60408051808201909152600e81526d696e666c6174696f6e207a65726f60901b60208201526001600160a01b0382166113815760405162461bcd60e51b815260040161059f919061276c565b506002546040517f4bdd1012a7d55ed9afad8675a125e1b68c7c15f712c0f3d5cddac69c3b979805916113bf9184916001600160a01b031690612752565b60405180910390a1600280546001600160a01b0319166001600160a01b038316179055600854610a9e576a295be96e6406697200000060085550565b600954620143700190565b60105460005b81811015610dbe576010805460009190600019810190811061142a57fe5b600091825260209091200154601080546001600160a01b039092169250908061144f57fe5b600082815260208120820160001990810180546001600160a01b03191690559091019091556040517f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb59916114a591849190612737565b60405180910390a15060010161140c565b600083836040516020016114cb9291906125a6565b60408051808303601f190181529181528151602092830120600081815260139093529120805491925090600160c01b90046001600160401b03166115b25760148054600180820183556000929092527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec01839055810180546001600160a01b0319166001600160a01b038716179055611565846040612095565b805161157b916002840191602090910190612359565b5060145460018201805467ffffffffffffffff60a01b1916600160a01b6000199093016001600160401b0316929092029190911790555b8054436001600160c01b038181166001600160401b03600160c01b80860482166001019091160291909316176001600160c01b0319169190911782556040517f7a459ed083a9b267865360013a5ad6dbc07e5befe6e4f71671c940fdd4206bee9161162391889190889088906125eb565b60405180910390a1600b80546001600160c01b0319811660016001600160c01b0392831681018316919091178084559301549216600160a01b9092046001600160401b0316600160c01b0291909117905550505050565b600060035443141561168e575060006104cd565b43600355600d544790811115611a645760006116b7600e54600d5461203290919063ffffffff16565b90508082141561182c57600e546005546116d19082612032565b6005556040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c906117049083906127a1565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561175c57600080fd5b505af19350505050801561176e575060015b6117dd5761177a6127b0565b806117855750611797565b611791308260006114b6565b506117d8565b6117d8306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006114b6565b611826565b6006546117ea9082612032565b6006556040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a9061181d9083906127a1565b60405180910390a15b50611a62565b8082101561188e57600061184b600d548461214990919063ffffffff16565b60075490915061185b9082612032565b6007556040517f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab3689061181d9083906127a1565b600e5460055461189d91612032565b600555600e54600d546000916118be916118b8908690612149565b90612149565b6007549091506118ce9082612032565b600755600e546040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c91611902916127a1565b60405180910390a17f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab3688160405161193991906127a1565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5600e546040518263ffffffff1660e01b81526004016000604051808303818588803b15801561199357600080fd5b505af1935050505080156119a5575060015b611a14576119b16127b0565b806119bc57506119ce565b6119c8308260006114b6565b50611a0f565b611a0f306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006114b6565b611a60565b600e54600654611a2391612032565b600655600e546040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a91611a57916127a1565b60405180910390a15b505b505b60105460005b81811015611e8357600060108281548110611a8157fe5b60009182526020808320909101546001600160a01b031680835260129091526040909120549091508015611b0d576001600160a01b0382166000908152601260205260409081902060001983019055517f9895eddb1e8569b1dae526135aa5cab97f982fdc3b0ff7e17920c95e3b9bda6290611b0090849084906125d2565b60405180910390a1611e79565b6001600160a01b038216600090815260116020526040812054905a90506204a768811015611b77577f9b5c4be38598cb8d8b6e07727d2303d1d9fc2dfc31ad323170f5ea4dcc1f914a858703604051611b6691906127a1565b60405180910390a150505050611e83565b620493df1981018215801590611b8c57508083105b15611b945750815b846001600160a01b0316636d0e8c34826040518263ffffffff1660e01b8152600401602060405180830381600088803b158015611bd057600080fd5b5087f193505050508015611c01575060408051601f3d908101601f19168201909252611bfe9181019061247c565b60015b611e3857611c0d6127b0565b80611c185750611c9f565b611c2586825a86036114b6565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611c6057600080fd5b505af1158015611c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c98919061247c565b5050611e33565b60005a9050600084118015611cbd575083611cba8483612149565b10155b15611d9057611cf1866040518060400160405280600a8152602001696f7574206f662067617360b01b8152508386036114b6565b6000866001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611d2e57600080fd5b505af1158015611d42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d66919061247c565b905080611d8a57600c546001600160a01b0388166000908152601260205260409020555b50611e31565b611dbc86604051806040016040528060078152602001663ab735b737bbb760c91b8152508386036114b6565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611df757600080fd5b505af1158015611e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e2f919061247c565b505b505b611e75565b507fe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4855a8403604051611e6c9291906125d2565b60405180910390a15b5050505b5050600101611a6a565b50611e8c6121a6565b92508215611ed657600e8390556040517f34f843cef0df42035141347873da1758a6643358831b5ba5b1580be947644f9290611ec99085906127a1565b60405180910390a1611edc565b6000600e555b505047600d5590565b60006104ca600754611f0460065460055461214990919063ffffffff16565b90612032565b6000808211611f53576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b83611f605750600061200b565b83830283858281611f6d57fe5b041415611f8657828181611f7d57fe5b0491505061200b565b6000838681611f9157fe5b0490506000848781611f9f57fe5b0690506000858781611fad57fe5b0490506000868881611fbb57fe5b069050612003611fd588611fcf86856121bf565b90612218565b611f04611fe286866121bf565b611f04611fef89876121bf565b611f048d611ffd8c8b6121bf565b906121bf565b955050505050505b9392505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76104ca8161227f565b60008282018381101561208c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60606000839050828151116120ad578391505061208f565b6000836001600160401b03811180156120c557600080fd5b506040519080825280601f01601f1916602001820160405280156120f0576020820181803683370190505b50905060005b848110156121405782818151811061210a57fe5b602001015160f81c60f81b82828151811061212157fe5b60200101906001600160f81b031916908160001a9053506001016120f6565b50949350505050565b6000828211156121a0576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006104ca60055460045461214990919063ffffffff16565b6000826121ce5750600061208f565b828202828482816121db57fe5b041461208c5760405162461bcd60e51b815260040180806020018281038252602181526020018061286a6021913960400191505060405180910390fd5b600080821161226e576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161227757fe5b049392505050565b600154600160a01b900460ff16156122d5576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261238f57600085556123d5565b82601f106123a857805160ff19168380011785556123d5565b828001600101855582156123d5579182015b828111156123d55782518255916020019190600101906123ba565b506108e79291505b808211156108e757600081556001016123dd565b600060208284031215612402578081fd5b813561208c81612854565b6000806020838503121561241f578081fd5b82356001600160401b0380821115612435578283fd5b818501915085601f830112612448578283fd5b813581811115612456578384fd5b86602060408302850101111561246a578384fd5b60209290920196919550909350505050565b60006020828403121561248d578081fd5b8151801515811461208c578182fd5b6000602082840312156124ad578081fd5b5035919050565b600080604083850312156124c6578182fd5b50508035926020909101359150565b6000815180845260208085019450808401835b8381101561250d5781516001600160a01b0316875295820195908201906001016124e8565b509495945050505050565b6000815180845260208085019450808401835b8381101561250d5781518752958201959082019060010161252b565b60008151808452815b8181101561256c57602081850181015186830182015201612550565b8181111561257d5782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03831681526040602082018190526000906125ca90830184612547565b949350505050565b6001600160a01b03929092168252602082015260400190565b600060018060a01b0386168252846020830152608060408301526126126080830185612547565b905082606083015295945050505050565b606080825284519082018190526000906020906080840190828801845b828110156126655781516001600160a01b031684529284019290840190600101612640565b505050838103828501526126798187612518565b915050828103604084015261268e8185612518565b9695505050505050565b600060a082526126ab60a0830188612518565b6020838203818501526126be8289612518565b848103604086015287518082529092508183019082810284018301838a01865b8381101561270c57601f198784030185526126fa838351612547565b948601949250908501906001016126de565b50508681036060880152612720818a6124d5565b955050505050508260808301529695505050505050565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b0392831681529116602082015260400190565b60006020825261200b6020830184612547565b6001600160c01b039290921682526001600160401b0316602082015260400190565b90815260200190565b60e01c90565b600060443d10156127c0576104cd565b600481823e6308c379a06127d482516127aa565b146127de576104cd565b6040513d600319016004823e80513d6001600160401b03816024840111818411171561280d57505050506104cd565b8284019250825191508082111561282757505050506104cd565b503d8301602082840101111561283f575050506104cd565b601f01601f1916810160200160405291505090565b6001600160a01b0381168114610a9e57600080fdfe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a26469706673582212204565821f65608e6e4fdb7076f78e5f829e603a82de85005cd73fca61d73e634f64736f6c63430007060033" + }, + "1000000000000000000000000000000000000003": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c80639d6a890f116100a2578063c373a08e11610071578063c373a08e1461051b578063c5adc53914610541578063c9f960eb1461066b578063d38bfff414610673578063ffacb84e1461069957610116565b80639d6a890f146104205780639d986f91146104465780639ec2b58114610472578063b39c68581461051357610116565b806371e1fad9116100e957806371e1fad9146102fd57806376794efb146103055780637ac420ad146103a85780638ab63380146103e05780638c9d28b61461041857610116565b80635aa6e6751461011b5780635d36b1901461013f57806360848b441461014957806360f7ac97146102f5575b600080fd5b6101236106f1565b604080516001600160a01b039092168252519081900360200190f35b610147610700565b005b6101476004803603608081101561015f57600080fd5b81359190810190604081016020820135600160201b81111561018057600080fd5b82018360208201111561019257600080fd5b803590602001918460208302840111600160201b831117156101b357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561020257600080fd5b82018360208201111561021457600080fd5b803590602001918460208302840111600160201b8311171561023557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561028457600080fd5b82018360208201111561029657600080fd5b803590602001918460208302840111600160201b831117156102b757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506107c2945050505050565b610123610dbe565b610123610dcd565b6101476004803603604081101561031b57600080fd5b810190602081018135600160201b81111561033557600080fd5b82018360208201111561034757600080fd5b803590602001918460208302840111600160201b8311171561036857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610ddc915050565b6103ce600480360360208110156103be57600080fd5b50356001600160a01b0316610ebd565b60408051918252519081900360200190f35b610147600480360360608110156103f657600080fd5b506001600160a01b038135811691602081013582169160409091013516610ed8565b610123610f68565b6101476004803603602081101561043657600080fd5b50356001600160a01b0316610f77565b6101476004803603604081101561045c57600080fd5b506001600160a01b038135169060200135610f79565b6101476004803603602081101561048857600080fd5b810190602081018135600160201b8111156104a257600080fd5b8201836020820111156104b457600080fd5b803590602001918460208302840111600160201b831117156104d557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611029945050505050565b610123611177565b6101476004803603602081101561053157600080fd5b50356001600160a01b0316611186565b6101476004803603606081101561055757600080fd5b81359190810190604081016020820135600160201b81111561057857600080fd5b82018360208201111561058a57600080fd5b803590602001918460208302840111600160201b831117156105ab57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156105fa57600080fd5b82018360208201111561060c57600080fd5b803590602001918460208302840111600160201b8311171561062d57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061122b945050505050565b6101236115e6565b6101476004803603602081101561068957600080fd5b50356001600160a01b031661160b565b6106a16116cd565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156106dd5781810151838201526020016106c5565b505050509050019250505060405180910390f35b6000546001600160a01b031681565b6001546001600160a01b0316331461074f576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b8251825160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146108825760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561084757818101518382015260200161082f565b50505050905090810190601f1680156108745780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50815181146040518060400160405280601a815260200179082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b815250906109065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528751602484015287516000946001600160a01b031693639cb47538938a939283926044019180860191028083838b5b8381101561096757818101518382015260200161094f565b505050509050019250505060006040518083038186803b15801561098a57600080fd5b505afa15801561099e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156109c757600080fd5b8101908080516040519392919084600160201b8211156109e657600080fd5b9083019060208201858111156109fb57600080fd5b82518660208202830111600160201b82111715610a1757600080fd5b82525081516020918201928201910280838360005b83811015610a44578181015183820152602001610a2c565b5050505091909101604090815233600090815260056020529081205495965093506000199250839150505b85811015610c9e576000898281518110610a8557fe5b60200260200101519050806001901b851660001415610b2e5783610b2e573360009081526007602052604090205460ff1615610ac45760019350610b2e565b604080518082018252600f81526e139bdd081dda1a5d195b1a5cdd1959608a1b6020808301918252925162461bcd60e51b8152600481019384528251602482015282519293928392604490920191908083836000831561084757818101518382015260200161082f565b600019831415610bd157858281518110610b4457fe5b60200260200101516001600160a01b031663f72cab28338d6040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ba257600080fd5b505af1158015610bb6573d6000803e3d6000fd5b505050506040513d6020811015610bcc57600080fd5b505192505b858281518110610bdd57fe5b60200260200101516001600160a01b03166355f7b69b338d8c8681518110610c0157fe5b60200260200101518c8781518110610c1557fe5b6020026020010151886040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182815260200195505050505050600060405180830381600087803b158015610c7957600080fd5b505af1158015610c8d573d6000803e3d6000fd5b505060019093019250610a6f915050565b5088336001600160a01b03167fa32444a31df2f9a116229eec3193d223a6bad89f7670ff17b8e5c7014a377da1868a8a4260405180806020018060200180602001858152602001848103845288818151815260200191508051906020019060200280838360005b83811015610d1d578181015183820152602001610d05565b50505050905001848103835287818151815260200191508051906020019060200280838360005b83811015610d5c578181015183820152602001610d44565b50505050905001848103825286818151815260200191508051906020019060200280838360005b83811015610d9b578181015183820152602001610d83565b5050505090500197505050505050505060405180910390a3505050505050505050565b6001546001600160a01b031681565b6004546001600160a01b031690565b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b03163314610e635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060005b8251811015610eb857816001901b1960056000858481518110610e8657fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002080549091169055600101610e67565b505050565b6001600160a01b031660009081526005602052604090205490565b6000546001600160a01b03163314610f29576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600280546001600160a01b039485166001600160a01b031991821617909155600480549385169382169390931790925560038054919093169116179055565b6002546001600160a01b031690565bfe5b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b031633146110005760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506001600160a01b0390911660009081526005602052604090208054600190921b919091179055565b60035460408051808201909152601081526f4654534f4d616e61676572206f6e6c7960801b6020820152906001600160a01b031633146110aa5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060065460005b8181101561110857600060076000600684815481106110cc57fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff19169115159190911790556001016110b1565b5050805160005b818110156111635760016007600085848151811061112957fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905560010161110f565b508151610eb8906006906020850190611809565b6003546001600160a01b031690565b6000546001600160a01b031633146111d7576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b8151815160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146112ae5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528651602484015286516000946001600160a01b031693639cb475389389939283926044019180860191028083838b5b8381101561130f5781810151838201526020016112f7565b505050509050019250505060006040518083038186803b15801561133257600080fd5b505afa158015611346573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561136f57600080fd5b8101908080516040519392919084600160201b82111561138e57600080fd5b9083019060208201858111156113a357600080fd5b82518660208202830111600160201b821117156113bf57600080fd5b82525081516020918201928201910280838360005b838110156113ec5781810151838201526020016113d4565b50505050919091016040908152336000908152600560205290812054959650935083925050505b8481101561150e57600087828151811061142957fe5b60200260200101519050806001901b8416600014156114645782611464573360009081526007602052604090205460ff1615610ac457600192505b84828151811061147057fe5b60200260200101516001600160a01b03166327bd2ad5338b8a868151811061149457fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050600060405180830381600087803b1580156114e957600080fd5b505af11580156114fd573d6000803e3d6000fd5b505060019093019250611413915050565b5086336001600160a01b03167f90c022ade239639b1f8c4ebb8a76df5e03a7129df46cf9bcdae3c1450ea35434858842604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015611588578181015183820152602001611570565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156115c75781810151838201526020016115af565b505050509050019550505050505060405180910390a350505050505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76116068161172f565b905090565b6000546001600160a01b0316331461165c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6060600680548060200260200160405190810160405280929190818152602001828054801561172557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611707575b5050505050905090565b600154600160a01b900460ff1615611785576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b82805482825590600052602060002090810192821561185e579160200282015b8281111561185e57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611829565b5061186a92915061186e565b5090565b5b8082111561186a576000815560010161186f56fea2646970667358221220c1d73dd5d713527b4b18f6ca6d3d915fe356fa5425b5776172fa3bf91a5476e564736f6c63430007060033" + }, + "2f1A24556db27d120d15CA0bf0F9adC76499d9C0": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "80b0a36609DeAff1373b5d35f901833a377f8d67": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "dDf6cDE14F7B455F7412530B727F73aDB722Cc56": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "554B291A6e2C585D20BA7bBD86F44df823C92Cd1": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "daE308584467d800eA6FF0EB732f1CA5411df7D7": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "Be6E2015209f428874E9C764Ad75aFd4E349A4Cc": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "E8bF1Aef0734ed65b3F8650C83bd4A3eb8Fc5457": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "2360a707c00fbE28bf2561fc013dD9D8750c6682": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "94f22De627a3e5092A2d10008bB80d4ccfb51f17": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"}, + "7510B5DedcF53536A1fbeb1cFF18fDb170DdfD7e": {"balance": "0x1999999999999999999999999999999999999999999999999999999999999999"} + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }` + + // CostonParams are the params used for the Coston test network. + CostonParams = Params{ + TxFeeConfig: TxFeeConfig{ + TxFee: units.MilliAvax, + CreateAssetTxFee: 10 * units.MilliAvax, + CreateSubnetTxFee: 100 * units.MilliAvax, + CreateBlockchainTxFee: 100 * units.MilliAvax, + }, + StakingConfig: StakingConfig{ + UptimeRequirement: .8, // 80% + MinValidatorStake: 1 * units.Avax, + MaxValidatorStake: 3 * units.MegaAvax, + MinDelegatorStake: 1 * units.Avax, + MinDelegationFee: 20000, // 2% + MinStakeDuration: 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + RewardConfig: reward.Config{ + MaxConsumptionRate: .12 * reward.PercentDenominator, + MinConsumptionRate: .10 * reward.PercentDenominator, + MintingPeriod: 365 * 24 * time.Hour, + SupplyCap: 720 * units.MegaAvax, + }, + }, + } +) diff --git a/avalanchego/genesis/genesis_local.go b/avalanchego/genesis/genesis_local.go index b08ffafe..766eca44 100644 --- a/avalanchego/genesis/genesis_local.go +++ b/avalanchego/genesis/genesis_local.go @@ -6,33 +6,78 @@ package genesis import ( "time" - _ "embed" - - "github.com/ava-labs/avalanchego/utils/cb58" - "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/platformvm/reward" -) - -// PrivateKey-vmRQiZeXEXYMyJhEiqdC2z5JhuDbxL8ix9UVvjgMu2Er1NepE => P-local1g65uqn6t77p656w64023nh8nd9updzmxyymev2 -// PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN => X-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u -// 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 => 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC - -const ( - VMRQKeyStr = "vmRQiZeXEXYMyJhEiqdC2z5JhuDbxL8ix9UVvjgMu2Er1NepE" - VMRQKeyFormattedStr = crypto.PrivateKeyPrefix + VMRQKeyStr - - EWOQKeyStr = "ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN" - EWOQKeyFormattedStr = crypto.PrivateKeyPrefix + EWOQKeyStr + // "github.com/flare-foundation/flare/utils/units" + // "github.com/flare-foundation/flare/vms/platformvm/reward" ) var ( - VMRQKey *crypto.PrivateKeySECP256K1R - EWOQKey *crypto.PrivateKeySECP256K1R + localGenesisConfigJSON = `{ + "networkID": 12345, + "allocations": [], + "startTime": 1630987200, + "initialStakeDuration": 31536000, + "initialStakeDurationOffset": 5400, + "initialStakedFunds": [], + "initialStakers": [], + "cChainGenesis": "", + "message": "flare" + }` - //go:embed genesis_local.json - localGenesisConfigJSON []byte + // localCChainGenesis is the C-Chain genesis block used for the local + // network. + localCChainGenesis = `{ + "config": { + "chainId": 4294967295, + "homesteadBlock": 0, + "daoForkBlock": 0, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "apricotPhase1BlockTimestamp": 0, + "apricotPhase2BlockTimestamp": 0, + "apricotPhase3BlockTimestamp": 0, + "apricotPhase4BlockTimestamp": 0, + "apricotPhase5BlockTimestamp": 0 + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x00", + "gasLimit": "0x5f5e100", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0100000000000000000000000000000000000000", + "alloc": { + "1000000000000000000000000000000000000001": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106100b45760003560e01c8063cfd1fdad11610071578063cfd1fdad1461015f578063eaebf6d3146101a2578063ec7424a0146101c7578063f417c9d8146101cf578063f5f59a4a146101d7578063f64b6fda146101df576100b4565b806329be4db2146100b95780634b8a125f146100e85780635f8c940d146100f057806371c5ecb1146100f857806371e24574146101155780637ff6faa61461013b575b600080fd5b6100d6600480360360208110156100cf57600080fd5b503561024f565b60408051918252519081900360200190f35b6100d661033f565b6100d6610347565b6100d66004803603602081101561010e57600080fd5b503561034c565b6100d66004803603602081101561012b57600080fd5b50356001600160a01b0316610364565b610143610379565b604080516001600160a01b039092168252519081900360200190f35b61018e6004803603608081101561017557600080fd5b508035906020810135906040810135906060013561037f565b604080519115158252519081900360200190f35b6101c5600480360360408110156101b857600080fd5b508035906020013561041d565b005b6100d66104c1565b6100d66104c7565b6100d66104cd565b6101c5600480360360208110156101f557600080fd5b81019060208101813564010000000081111561021057600080fd5b82018360208201111561022257600080fd5b8035906020019184600183028401116401000000008311171561024457600080fd5b5090925090506104d2565b60006001821161025e57600080fd5b3360009081526020819052604090206009015460001983019081111561028357600080fd5b33600090815260208190526040812060038306600381106102a057fe5b6003908102919091016002015433600090815260208190526040812091935091600019850106600381106102d057fe5b60030201600101549050816040516020018082815260200191505060405160208183030381529060405280519060200120811461030c57600080fd5b33600090815260208190526040812060036000198601066003811061032d57fe5b60030201549290921895945050505050565b636184740081565b600381565b600281611a40811061035d57600080fd5b0154905081565b60006020819052908152604090206009015481565b61dead81565b6000605a63618473ff19420104851461039757600080fd5b33600081815260208181526040808320600981018a905581516060810183528981528084018990529182018790529383529190529060038706600381106103da57fe5b6003020160008201518160000155602082015181600101556040820151816002015590505060015485111561041157506001610415565b5060005b949350505050565b6001821161042a57600080fd5b605a63618473ff19420104821461044057600080fd5b600154821161044e57600080fd5b334114801561045e57504161dead145b156104bd576001829055806002611a40600019850106611a40811061047f57fe5b0155604080518381526020810183905281517f8ffd19aa79a62d0764e560d21b1245698310783be781d7d80b38233d4d7d288c929181900390910190a15b5050565b60015481565b611a4081565b605a81565b7f5a4fad455fbfa0bb0f22d912bbfa4ef3d0887bb6933bffaf0f1f3e9fe1a12ca142838360405180848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f1916909201829003965090945050505050a1505056fea264697066735822122093876c1f2ec0c4be61ccfd1ee6b192af3d6daff617d10ecf09f29bea82c4570564736f6c63430007060033" + }, + "1000000000000000000000000000000000000002": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c80638be2fb8611610104578063d38bfff4116100a2578063e9de7d6011610071578063e9de7d6014610322578063ecdda0dd14610335578063ed21b6e414610348578063ee323c921461035f576101cf565b8063d38bfff4146102e9578063d48a38df146102fc578063dded1b4714610304578063e371aef01461030c576101cf565b8063a6817ace116100de578063a6817ace146102be578063be0522e0146102c6578063c373a08e146102ce578063c9f960eb146102e1576101cf565b80638be2fb861461029b5780638ccf77a0146102a35780639d6a890f146102ab576101cf565b806362da19a511610171578063689c49991161014b578063689c49991461026557806372993615146102785780637fec8d3814610280578063870196b814610288576101cf565b806362da19a51461023c578063639031431461024457806363d4a53a1461024c576101cf565b80635042916c116101ad5780635042916c146102025780635aa6e675146102175780635d36b1901461022c57806360f7ac9714610234576101cf565b806310663750146101d45780631d76dea1146101f25780634f6a77b5146101fa575b600080fd5b6101dc610372565b6040516101e991906127a1565b60405180910390f35b6101dc610378565b6101dc61037e565b61021561021036600461249c565b610384565b005b61021f6103da565b6040516101e99190612592565b6102156103e9565b61021f6104ab565b6101dc6104ba565b6101dc6104c0565b6102546104d0565b6040516101e9959493929190612698565b61021561027336600461240d565b61050b565b6101dc61083a565b6101dc610840565b61021561029636600461249c565b6108eb565b6101dc610a3b565b610215610a41565b6102156102b93660046123f1565b610a9c565b6101dc610aa1565b61021f610aa7565b6102156102dc3660046123f1565b610ab6565b61021f610b5b565b6102156102f73660046123f1565b610b9a565b6101dc610c5c565b6101dc610c62565b610314610c68565b6040516101e992919061277f565b61021561033036600461249c565b610c89565b6102546103433660046124b4565b610dc2565b61035061111f565b6040516101e993929190612623565b61021561036d3660046123f1565b6112e4565b60055481565b60095481565b60085481565b6000546001600160a01b031633146103d5576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600c55565b6000546001600160a01b031681565b6001546001600160a01b03163314610438576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6001546001600160a01b031681565b60045481565b60006104ca6113fb565b90505b90565b600b546060908190819081906000906104fa90600160c01b90046001600160401b03166001610dc2565b945094509450945094509091929394565b6000546001600160a01b0316331461055c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b604080518082019091526008815267746f6f206d616e7960c01b60208201528190600a8211156105a85760405162461bcd60e51b815260040161059f919061276c565b60405180910390fd5b506105b1611406565b60005b818110156108345760008484838181106105ca57fe5b6105e092602060409092020190810191506123f1565b6001600160a01b031614156040518060400160405280600c81526020016b61646472657373207a65726f60a01b8152509061062e5760405162461bcd60e51b815260040161059f919061276c565b5060105460005b818110156106d7576010818154811061064a57fe5b6000918252602090912001546001600160a01b031686868581811061066b57fe5b61068192602060409092020190810191506123f1565b6001600160a01b031614156040518060400160405280600b81526020016a647570206164647265737360a81b815250906106ce5760405162461bcd60e51b815260040161059f919061276c565b50600101610635565b5060108585848181106106e657fe5b6106fc92602060409092020190810191506123f1565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905584848381811061073a57fe5b905060400201602001356011600087878681811061075457fe5b61076a92602060409092020190810191506123f1565b6001600160a01b03166001600160a01b03168152602001908152602001600020819055506000601260008787868181106107a057fe5b6107b692602060409092020190810191506123f1565b6001600160a01b031681526020810191909152604001600020557f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb598585848181106107fd57fe5b61081392602060409092020190810191506123f1565b6001604051610823929190612737565b60405180910390a1506001016105b4565b50505050565b60075481565b6002546000906001600160a01b031661088557610885306040518060400160405280600e81526020016d696e666c6174696f6e207a65726f60901b81525060006114b6565b60026001609c1b01331461089857600080fd5b6108a061167a565b905060006108ac611ee5565b90504781146108e7576108e7306040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b81525060006114b6565b5090565b6000546001600160a01b0316331461093c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60085461094c90606e6064611f0a565b811115604051806040016040528060118152602001700dac2f040dad2dce840e8dede40d0d2ced607b1b815250906109975760405162461bcd60e51b815260040161059f919061276c565b5060408051808201909152601081526f6d6178206d696e74206973207a65726f60801b6020820152816109dd5760405162461bcd60e51b815260040161059f919061276c565b5062015180600a54014211604051806040016040528060128152602001711d1a5b594819d85c081d1bdbc81cda1bdc9d60721b81525090610a315760405162461bcd60e51b815260040161059f919061276c565b5060085542600a55565b60035481565b6000546001600160a01b03163314610a92576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b610a9a611406565b565bfe5b50565b600a5481565b6002546001600160a01b031681565b6000546001600160a01b03163314610b07576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b600f5460009060ff16610b8857600f805460ff191660011790556000610b7f612012565b91506104cd9050565b506000546001600160a01b03166104cd565b6000546001600160a01b03163314610beb576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b600c5481565b60065481565b600b546001600160c01b03811690600160c01b90046001600160401b031682565b60025460408051808201909152600d81526c3737ba1034b7333630ba34b7b760991b602082015233916001600160a01b03168214610cda5760405162461bcd60e51b815260040161059f919061276c565b5060085482111560405180604001604052806007815260200166746f6f2062696760c81b81525090610d1f5760405162461bcd60e51b815260040161059f919061276c565b5042610d296113fb565b10604051806040016040528060098152602001683a37b79037b33a32b760b91b81525090610d6a5760405162461bcd60e51b815260040161059f919061276c565b508115610dbe5742600955600454610d829083612032565b6004556040517f4c4f1efc376f31abeb51b72c5f9ed81cf4016591312bb02337e58149dcfaaab490610db59084906127a1565b60405180910390a15b5050565b606080606080600060148054905087106040518060400160405280601081526020016f0e6e8c2e4e840d2dcc8caf040d0d2ced60831b81525090610e195760405162461bcd60e51b815260040161059f919061276c565b506014546000908888011115610e3457601454889003610e36565b865b9050806001600160401b0381118015610e4e57600080fd5b50604051908082528060200260200182016040528015610e78578160200160208202803683370190505b509550806001600160401b0381118015610e9157600080fd5b50604051908082528060200260200182016040528015610ebb578160200160208202803683370190505b509450806001600160401b0381118015610ed457600080fd5b50604051908082528060200260200182016040528015610f0857816020015b6060815260200190600190039081610ef35790505b509350806001600160401b0381118015610f2157600080fd5b50604051908082528060200260200182016040528015610f4b578160200160208202803683370190505b50925060005b818110156111035760006014828b0181548110610f6a57fe5b6000918252602080832090910154808352601390915260409091205489519192506001600160c01b031690899084908110610fa157fe5b6020026020010181815250506013600082815260200190815260200160002060000160189054906101000a90046001600160401b03166001600160401b0316878381518110610fec57fe5b6020908102919091018101919091526000828152601382526040908190206002908101805483516001821615610100026000190190911692909204601f810185900485028301850190935282825290929091908301828280156110905780601f1061106557610100808354040283529160200191611090565b820191906000526020600020905b81548152906001019060200180831161107357829003601f168201915b50505050508683815181106110a157fe5b60200260200101819052506013600082815260200190815260200160002060010160009054906101000a90046001600160a01b03168583815181106110e257fe5b6001600160a01b039092166020928302919091019091015250600101610f51565b5050600b549497939650919450926001600160c01b0316919050565b60105460609081908190806001600160401b038111801561113f57600080fd5b50604051908082528060200260200182016040528015611169578160200160208202803683370190505b509350806001600160401b038111801561118257600080fd5b506040519080825280602002602001820160405280156111ac578160200160208202803683370190505b509250806001600160401b03811180156111c557600080fd5b506040519080825280602002602001820160405280156111ef578160200160208202803683370190505b50915060005b818110156112dd5760006010828154811061120c57fe5b9060005260206000200160009054906101000a90046001600160a01b031690508086838151811061123957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060116000826001600160a01b03166001600160a01b031681526020019081526020016000205485838151811061128b57fe5b60200260200101818152505060126000826001600160a01b03166001600160a01b03168152602001908152602001600020548483815181106112c957fe5b6020908102919091010152506001016111f5565b5050909192565b6000546001600160a01b03163314611335576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60408051808201909152600e81526d696e666c6174696f6e207a65726f60901b60208201526001600160a01b0382166113815760405162461bcd60e51b815260040161059f919061276c565b506002546040517f4bdd1012a7d55ed9afad8675a125e1b68c7c15f712c0f3d5cddac69c3b979805916113bf9184916001600160a01b031690612752565b60405180910390a1600280546001600160a01b0319166001600160a01b038316179055600854610a9e576a295be96e6406697200000060085550565b600954620143700190565b60105460005b81811015610dbe576010805460009190600019810190811061142a57fe5b600091825260209091200154601080546001600160a01b039092169250908061144f57fe5b600082815260208120820160001990810180546001600160a01b03191690559091019091556040517f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb59916114a591849190612737565b60405180910390a15060010161140c565b600083836040516020016114cb9291906125a6565b60408051808303601f190181529181528151602092830120600081815260139093529120805491925090600160c01b90046001600160401b03166115b25760148054600180820183556000929092527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec01839055810180546001600160a01b0319166001600160a01b038716179055611565846040612095565b805161157b916002840191602090910190612359565b5060145460018201805467ffffffffffffffff60a01b1916600160a01b6000199093016001600160401b0316929092029190911790555b8054436001600160c01b038181166001600160401b03600160c01b80860482166001019091160291909316176001600160c01b0319169190911782556040517f7a459ed083a9b267865360013a5ad6dbc07e5befe6e4f71671c940fdd4206bee9161162391889190889088906125eb565b60405180910390a1600b80546001600160c01b0319811660016001600160c01b0392831681018316919091178084559301549216600160a01b9092046001600160401b0316600160c01b0291909117905550505050565b600060035443141561168e575060006104cd565b43600355600d544790811115611a645760006116b7600e54600d5461203290919063ffffffff16565b90508082141561182c57600e546005546116d19082612032565b6005556040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c906117049083906127a1565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561175c57600080fd5b505af19350505050801561176e575060015b6117dd5761177a6127b0565b806117855750611797565b611791308260006114b6565b506117d8565b6117d8306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006114b6565b611826565b6006546117ea9082612032565b6006556040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a9061181d9083906127a1565b60405180910390a15b50611a62565b8082101561188e57600061184b600d548461214990919063ffffffff16565b60075490915061185b9082612032565b6007556040517f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab3689061181d9083906127a1565b600e5460055461189d91612032565b600555600e54600d546000916118be916118b8908690612149565b90612149565b6007549091506118ce9082612032565b600755600e546040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c91611902916127a1565b60405180910390a17f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab3688160405161193991906127a1565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5600e546040518263ffffffff1660e01b81526004016000604051808303818588803b15801561199357600080fd5b505af1935050505080156119a5575060015b611a14576119b16127b0565b806119bc57506119ce565b6119c8308260006114b6565b50611a0f565b611a0f306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006114b6565b611a60565b600e54600654611a2391612032565b600655600e546040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a91611a57916127a1565b60405180910390a15b505b505b60105460005b81811015611e8357600060108281548110611a8157fe5b60009182526020808320909101546001600160a01b031680835260129091526040909120549091508015611b0d576001600160a01b0382166000908152601260205260409081902060001983019055517f9895eddb1e8569b1dae526135aa5cab97f982fdc3b0ff7e17920c95e3b9bda6290611b0090849084906125d2565b60405180910390a1611e79565b6001600160a01b038216600090815260116020526040812054905a90506204a768811015611b77577f9b5c4be38598cb8d8b6e07727d2303d1d9fc2dfc31ad323170f5ea4dcc1f914a858703604051611b6691906127a1565b60405180910390a150505050611e83565b620493df1981018215801590611b8c57508083105b15611b945750815b846001600160a01b0316636d0e8c34826040518263ffffffff1660e01b8152600401602060405180830381600088803b158015611bd057600080fd5b5087f193505050508015611c01575060408051601f3d908101601f19168201909252611bfe9181019061247c565b60015b611e3857611c0d6127b0565b80611c185750611c9f565b611c2586825a86036114b6565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611c6057600080fd5b505af1158015611c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c98919061247c565b5050611e33565b60005a9050600084118015611cbd575083611cba8483612149565b10155b15611d9057611cf1866040518060400160405280600a8152602001696f7574206f662067617360b01b8152508386036114b6565b6000866001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611d2e57600080fd5b505af1158015611d42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d66919061247c565b905080611d8a57600c546001600160a01b0388166000908152601260205260409020555b50611e31565b611dbc86604051806040016040528060078152602001663ab735b737bbb760c91b8152508386036114b6565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611df757600080fd5b505af1158015611e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e2f919061247c565b505b505b611e75565b507fe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4855a8403604051611e6c9291906125d2565b60405180910390a15b5050505b5050600101611a6a565b50611e8c6121a6565b92508215611ed657600e8390556040517f34f843cef0df42035141347873da1758a6643358831b5ba5b1580be947644f9290611ec99085906127a1565b60405180910390a1611edc565b6000600e555b505047600d5590565b60006104ca600754611f0460065460055461214990919063ffffffff16565b90612032565b6000808211611f53576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b83611f605750600061200b565b83830283858281611f6d57fe5b041415611f8657828181611f7d57fe5b0491505061200b565b6000838681611f9157fe5b0490506000848781611f9f57fe5b0690506000858781611fad57fe5b0490506000868881611fbb57fe5b069050612003611fd588611fcf86856121bf565b90612218565b611f04611fe286866121bf565b611f04611fef89876121bf565b611f048d611ffd8c8b6121bf565b906121bf565b955050505050505b9392505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76104ca8161227f565b60008282018381101561208c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60606000839050828151116120ad578391505061208f565b6000836001600160401b03811180156120c557600080fd5b506040519080825280601f01601f1916602001820160405280156120f0576020820181803683370190505b50905060005b848110156121405782818151811061210a57fe5b602001015160f81c60f81b82828151811061212157fe5b60200101906001600160f81b031916908160001a9053506001016120f6565b50949350505050565b6000828211156121a0576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006104ca60055460045461214990919063ffffffff16565b6000826121ce5750600061208f565b828202828482816121db57fe5b041461208c5760405162461bcd60e51b815260040180806020018281038252602181526020018061286a6021913960400191505060405180910390fd5b600080821161226e576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161227757fe5b049392505050565b600154600160a01b900460ff16156122d5576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261238f57600085556123d5565b82601f106123a857805160ff19168380011785556123d5565b828001600101855582156123d5579182015b828111156123d55782518255916020019190600101906123ba565b506108e79291505b808211156108e757600081556001016123dd565b600060208284031215612402578081fd5b813561208c81612854565b6000806020838503121561241f578081fd5b82356001600160401b0380821115612435578283fd5b818501915085601f830112612448578283fd5b813581811115612456578384fd5b86602060408302850101111561246a578384fd5b60209290920196919550909350505050565b60006020828403121561248d578081fd5b8151801515811461208c578182fd5b6000602082840312156124ad578081fd5b5035919050565b600080604083850312156124c6578182fd5b50508035926020909101359150565b6000815180845260208085019450808401835b8381101561250d5781516001600160a01b0316875295820195908201906001016124e8565b509495945050505050565b6000815180845260208085019450808401835b8381101561250d5781518752958201959082019060010161252b565b60008151808452815b8181101561256c57602081850181015186830182015201612550565b8181111561257d5782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03831681526040602082018190526000906125ca90830184612547565b949350505050565b6001600160a01b03929092168252602082015260400190565b600060018060a01b0386168252846020830152608060408301526126126080830185612547565b905082606083015295945050505050565b606080825284519082018190526000906020906080840190828801845b828110156126655781516001600160a01b031684529284019290840190600101612640565b505050838103828501526126798187612518565b915050828103604084015261268e8185612518565b9695505050505050565b600060a082526126ab60a0830188612518565b6020838203818501526126be8289612518565b848103604086015287518082529092508183019082810284018301838a01865b8381101561270c57601f198784030185526126fa838351612547565b948601949250908501906001016126de565b50508681036060880152612720818a6124d5565b955050505050508260808301529695505050505050565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b0392831681529116602082015260400190565b60006020825261200b6020830184612547565b6001600160c01b039290921682526001600160401b0316602082015260400190565b90815260200190565b60e01c90565b600060443d10156127c0576104cd565b600481823e6308c379a06127d482516127aa565b146127de576104cd565b6040513d600319016004823e80513d6001600160401b03816024840111818411171561280d57505050506104cd565b8284019250825191508082111561282757505050506104cd565b503d8301602082840101111561283f575050506104cd565b601f01601f1916810160200160405291505090565b6001600160a01b0381168114610a9e57600080fdfe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a26469706673582212204565821f65608e6e4fdb7076f78e5f829e603a82de85005cd73fca61d73e634f64736f6c63430007060033" + }, + "1000000000000000000000000000000000000003": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c80639d6a890f116100a2578063c373a08e11610071578063c373a08e1461051b578063c5adc53914610541578063c9f960eb1461066b578063d38bfff414610673578063ffacb84e1461069957610116565b80639d6a890f146104205780639d986f91146104465780639ec2b58114610472578063b39c68581461051357610116565b806371e1fad9116100e957806371e1fad9146102fd57806376794efb146103055780637ac420ad146103a85780638ab63380146103e05780638c9d28b61461041857610116565b80635aa6e6751461011b5780635d36b1901461013f57806360848b441461014957806360f7ac97146102f5575b600080fd5b6101236106f1565b604080516001600160a01b039092168252519081900360200190f35b610147610700565b005b6101476004803603608081101561015f57600080fd5b81359190810190604081016020820135600160201b81111561018057600080fd5b82018360208201111561019257600080fd5b803590602001918460208302840111600160201b831117156101b357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561020257600080fd5b82018360208201111561021457600080fd5b803590602001918460208302840111600160201b8311171561023557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561028457600080fd5b82018360208201111561029657600080fd5b803590602001918460208302840111600160201b831117156102b757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506107c2945050505050565b610123610dbe565b610123610dcd565b6101476004803603604081101561031b57600080fd5b810190602081018135600160201b81111561033557600080fd5b82018360208201111561034757600080fd5b803590602001918460208302840111600160201b8311171561036857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610ddc915050565b6103ce600480360360208110156103be57600080fd5b50356001600160a01b0316610ebd565b60408051918252519081900360200190f35b610147600480360360608110156103f657600080fd5b506001600160a01b038135811691602081013582169160409091013516610ed8565b610123610f68565b6101476004803603602081101561043657600080fd5b50356001600160a01b0316610f77565b6101476004803603604081101561045c57600080fd5b506001600160a01b038135169060200135610f79565b6101476004803603602081101561048857600080fd5b810190602081018135600160201b8111156104a257600080fd5b8201836020820111156104b457600080fd5b803590602001918460208302840111600160201b831117156104d557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611029945050505050565b610123611177565b6101476004803603602081101561053157600080fd5b50356001600160a01b0316611186565b6101476004803603606081101561055757600080fd5b81359190810190604081016020820135600160201b81111561057857600080fd5b82018360208201111561058a57600080fd5b803590602001918460208302840111600160201b831117156105ab57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156105fa57600080fd5b82018360208201111561060c57600080fd5b803590602001918460208302840111600160201b8311171561062d57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061122b945050505050565b6101236115e6565b6101476004803603602081101561068957600080fd5b50356001600160a01b031661160b565b6106a16116cd565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156106dd5781810151838201526020016106c5565b505050509050019250505060405180910390f35b6000546001600160a01b031681565b6001546001600160a01b0316331461074f576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b8251825160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146108825760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561084757818101518382015260200161082f565b50505050905090810190601f1680156108745780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50815181146040518060400160405280601a815260200179082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b815250906109065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528751602484015287516000946001600160a01b031693639cb47538938a939283926044019180860191028083838b5b8381101561096757818101518382015260200161094f565b505050509050019250505060006040518083038186803b15801561098a57600080fd5b505afa15801561099e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156109c757600080fd5b8101908080516040519392919084600160201b8211156109e657600080fd5b9083019060208201858111156109fb57600080fd5b82518660208202830111600160201b82111715610a1757600080fd5b82525081516020918201928201910280838360005b83811015610a44578181015183820152602001610a2c565b5050505091909101604090815233600090815260056020529081205495965093506000199250839150505b85811015610c9e576000898281518110610a8557fe5b60200260200101519050806001901b851660001415610b2e5783610b2e573360009081526007602052604090205460ff1615610ac45760019350610b2e565b604080518082018252600f81526e139bdd081dda1a5d195b1a5cdd1959608a1b6020808301918252925162461bcd60e51b8152600481019384528251602482015282519293928392604490920191908083836000831561084757818101518382015260200161082f565b600019831415610bd157858281518110610b4457fe5b60200260200101516001600160a01b031663f72cab28338d6040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ba257600080fd5b505af1158015610bb6573d6000803e3d6000fd5b505050506040513d6020811015610bcc57600080fd5b505192505b858281518110610bdd57fe5b60200260200101516001600160a01b03166355f7b69b338d8c8681518110610c0157fe5b60200260200101518c8781518110610c1557fe5b6020026020010151886040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182815260200195505050505050600060405180830381600087803b158015610c7957600080fd5b505af1158015610c8d573d6000803e3d6000fd5b505060019093019250610a6f915050565b5088336001600160a01b03167fa32444a31df2f9a116229eec3193d223a6bad89f7670ff17b8e5c7014a377da1868a8a4260405180806020018060200180602001858152602001848103845288818151815260200191508051906020019060200280838360005b83811015610d1d578181015183820152602001610d05565b50505050905001848103835287818151815260200191508051906020019060200280838360005b83811015610d5c578181015183820152602001610d44565b50505050905001848103825286818151815260200191508051906020019060200280838360005b83811015610d9b578181015183820152602001610d83565b5050505090500197505050505050505060405180910390a3505050505050505050565b6001546001600160a01b031681565b6004546001600160a01b031690565b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b03163314610e635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060005b8251811015610eb857816001901b1960056000858481518110610e8657fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002080549091169055600101610e67565b505050565b6001600160a01b031660009081526005602052604090205490565b6000546001600160a01b03163314610f29576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600280546001600160a01b039485166001600160a01b031991821617909155600480549385169382169390931790925560038054919093169116179055565b6002546001600160a01b031690565bfe5b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b031633146110005760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506001600160a01b0390911660009081526005602052604090208054600190921b919091179055565b60035460408051808201909152601081526f4654534f4d616e61676572206f6e6c7960801b6020820152906001600160a01b031633146110aa5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060065460005b8181101561110857600060076000600684815481106110cc57fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff19169115159190911790556001016110b1565b5050805160005b818110156111635760016007600085848151811061112957fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905560010161110f565b508151610eb8906006906020850190611809565b6003546001600160a01b031690565b6000546001600160a01b031633146111d7576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b8151815160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146112ae5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528651602484015286516000946001600160a01b031693639cb475389389939283926044019180860191028083838b5b8381101561130f5781810151838201526020016112f7565b505050509050019250505060006040518083038186803b15801561133257600080fd5b505afa158015611346573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561136f57600080fd5b8101908080516040519392919084600160201b82111561138e57600080fd5b9083019060208201858111156113a357600080fd5b82518660208202830111600160201b821117156113bf57600080fd5b82525081516020918201928201910280838360005b838110156113ec5781810151838201526020016113d4565b50505050919091016040908152336000908152600560205290812054959650935083925050505b8481101561150e57600087828151811061142957fe5b60200260200101519050806001901b8416600014156114645782611464573360009081526007602052604090205460ff1615610ac457600192505b84828151811061147057fe5b60200260200101516001600160a01b03166327bd2ad5338b8a868151811061149457fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050600060405180830381600087803b1580156114e957600080fd5b505af11580156114fd573d6000803e3d6000fd5b505060019093019250611413915050565b5086336001600160a01b03167f90c022ade239639b1f8c4ebb8a76df5e03a7129df46cf9bcdae3c1450ea35434858842604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015611588578181015183820152602001611570565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156115c75781810151838201526020016115af565b505050509050019550505050505060405180910390a350505050505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76116068161172f565b905090565b6000546001600160a01b0316331461165c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6060600680548060200260200160405190810160405280929190818152602001828054801561172557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611707575b5050505050905090565b600154600160a01b900460ff1615611785576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b82805482825590600052602060002090810192821561185e579160200282015b8281111561185e57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611829565b5061186a92915061186e565b5090565b5b8082111561186a576000815560010161186f56fea2646970667358221220c1d73dd5d713527b4b18f6ca6d3d915fe356fa5425b5776172fa3bf91a5476e564736f6c63430007060033" + }, + "1000000000000000000000000000000000000003": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c80639d6a890f116100a2578063c373a08e11610071578063c373a08e1461051b578063c5adc53914610541578063c9f960eb1461066b578063d38bfff414610673578063ffacb84e1461069957610116565b80639d6a890f146104205780639d986f91146104465780639ec2b58114610472578063b39c68581461051357610116565b806371e1fad9116100e957806371e1fad9146102fd57806376794efb146103055780637ac420ad146103a85780638ab63380146103e05780638c9d28b61461041857610116565b80635aa6e6751461011b5780635d36b1901461013f57806360848b441461014957806360f7ac97146102f5575b600080fd5b6101236106f1565b604080516001600160a01b039092168252519081900360200190f35b610147610700565b005b6101476004803603608081101561015f57600080fd5b81359190810190604081016020820135600160201b81111561018057600080fd5b82018360208201111561019257600080fd5b803590602001918460208302840111600160201b831117156101b357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561020257600080fd5b82018360208201111561021457600080fd5b803590602001918460208302840111600160201b8311171561023557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561028457600080fd5b82018360208201111561029657600080fd5b803590602001918460208302840111600160201b831117156102b757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506107c2945050505050565b610123610dbe565b610123610dcd565b6101476004803603604081101561031b57600080fd5b810190602081018135600160201b81111561033557600080fd5b82018360208201111561034757600080fd5b803590602001918460208302840111600160201b8311171561036857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610ddc915050565b6103ce600480360360208110156103be57600080fd5b50356001600160a01b0316610ebd565b60408051918252519081900360200190f35b610147600480360360608110156103f657600080fd5b506001600160a01b038135811691602081013582169160409091013516610ed8565b610123610f68565b6101476004803603602081101561043657600080fd5b50356001600160a01b0316610f77565b6101476004803603604081101561045c57600080fd5b506001600160a01b038135169060200135610f79565b6101476004803603602081101561048857600080fd5b810190602081018135600160201b8111156104a257600080fd5b8201836020820111156104b457600080fd5b803590602001918460208302840111600160201b831117156104d557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611029945050505050565b610123611177565b6101476004803603602081101561053157600080fd5b50356001600160a01b0316611186565b6101476004803603606081101561055757600080fd5b81359190810190604081016020820135600160201b81111561057857600080fd5b82018360208201111561058a57600080fd5b803590602001918460208302840111600160201b831117156105ab57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156105fa57600080fd5b82018360208201111561060c57600080fd5b803590602001918460208302840111600160201b8311171561062d57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061122b945050505050565b6101236115e6565b6101476004803603602081101561068957600080fd5b50356001600160a01b031661160b565b6106a16116cd565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156106dd5781810151838201526020016106c5565b505050509050019250505060405180910390f35b6000546001600160a01b031681565b6001546001600160a01b0316331461074f576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b8251825160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146108825760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561084757818101518382015260200161082f565b50505050905090810190601f1680156108745780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50815181146040518060400160405280601a815260200179082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b815250906109065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528751602484015287516000946001600160a01b031693639cb47538938a939283926044019180860191028083838b5b8381101561096757818101518382015260200161094f565b505050509050019250505060006040518083038186803b15801561098a57600080fd5b505afa15801561099e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156109c757600080fd5b8101908080516040519392919084600160201b8211156109e657600080fd5b9083019060208201858111156109fb57600080fd5b82518660208202830111600160201b82111715610a1757600080fd5b82525081516020918201928201910280838360005b83811015610a44578181015183820152602001610a2c565b5050505091909101604090815233600090815260056020529081205495965093506000199250839150505b85811015610c9e576000898281518110610a8557fe5b60200260200101519050806001901b851660001415610b2e5783610b2e573360009081526007602052604090205460ff1615610ac45760019350610b2e565b604080518082018252600f81526e139bdd081dda1a5d195b1a5cdd1959608a1b6020808301918252925162461bcd60e51b8152600481019384528251602482015282519293928392604490920191908083836000831561084757818101518382015260200161082f565b600019831415610bd157858281518110610b4457fe5b60200260200101516001600160a01b031663f72cab28338d6040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ba257600080fd5b505af1158015610bb6573d6000803e3d6000fd5b505050506040513d6020811015610bcc57600080fd5b505192505b858281518110610bdd57fe5b60200260200101516001600160a01b03166355f7b69b338d8c8681518110610c0157fe5b60200260200101518c8781518110610c1557fe5b6020026020010151886040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182815260200195505050505050600060405180830381600087803b158015610c7957600080fd5b505af1158015610c8d573d6000803e3d6000fd5b505060019093019250610a6f915050565b5088336001600160a01b03167fa32444a31df2f9a116229eec3193d223a6bad89f7670ff17b8e5c7014a377da1868a8a4260405180806020018060200180602001858152602001848103845288818151815260200191508051906020019060200280838360005b83811015610d1d578181015183820152602001610d05565b50505050905001848103835287818151815260200191508051906020019060200280838360005b83811015610d5c578181015183820152602001610d44565b50505050905001848103825286818151815260200191508051906020019060200280838360005b83811015610d9b578181015183820152602001610d83565b5050505090500197505050505050505060405180910390a3505050505050505050565b6001546001600160a01b031681565b6004546001600160a01b031690565b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b03163314610e635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060005b8251811015610eb857816001901b1960056000858481518110610e8657fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002080549091169055600101610e67565b505050565b6001600160a01b031660009081526005602052604090205490565b6000546001600160a01b03163314610f29576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600280546001600160a01b039485166001600160a01b031991821617909155600480549385169382169390931790925560038054919093169116179055565b6002546001600160a01b031690565bfe5b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b031633146110005760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506001600160a01b0390911660009081526005602052604090208054600190921b919091179055565b60035460408051808201909152601081526f4654534f4d616e61676572206f6e6c7960801b6020820152906001600160a01b031633146110aa5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060065460005b8181101561110857600060076000600684815481106110cc57fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff19169115159190911790556001016110b1565b5050805160005b818110156111635760016007600085848151811061112957fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905560010161110f565b508151610eb8906006906020850190611809565b6003546001600160a01b031690565b6000546001600160a01b031633146111d7576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b8151815160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146112ae5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528651602484015286516000946001600160a01b031693639cb475389389939283926044019180860191028083838b5b8381101561130f5781810151838201526020016112f7565b505050509050019250505060006040518083038186803b15801561133257600080fd5b505afa158015611346573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561136f57600080fd5b8101908080516040519392919084600160201b82111561138e57600080fd5b9083019060208201858111156113a357600080fd5b82518660208202830111600160201b821117156113bf57600080fd5b82525081516020918201928201910280838360005b838110156113ec5781810151838201526020016113d4565b50505050919091016040908152336000908152600560205290812054959650935083925050505b8481101561150e57600087828151811061142957fe5b60200260200101519050806001901b8416600014156114645782611464573360009081526007602052604090205460ff1615610ac457600192505b84828151811061147057fe5b60200260200101516001600160a01b03166327bd2ad5338b8a868151811061149457fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050600060405180830381600087803b1580156114e957600080fd5b505af11580156114fd573d6000803e3d6000fd5b505060019093019250611413915050565b5086336001600160a01b03167f90c022ade239639b1f8c4ebb8a76df5e03a7129df46cf9bcdae3c1450ea35434858842604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015611588578181015183820152602001611570565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156115c75781810151838201526020016115af565b505050509050019550505050505060405180910390a350505050505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76116068161172f565b905090565b6000546001600160a01b0316331461165c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6060600680548060200260200160405190810160405280929190818152602001828054801561172557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611707575b5050505050905090565b600154600160a01b900460ff1615611785576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b82805482825590600052602060002090810192821561185e579160200282015b8281111561185e57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611829565b5061186a92915061186e565b5090565b5b8082111561186a576000815560010161186f56fea2646970667358221220c1d73dd5d713527b4b18f6ca6d3d915fe356fa5425b5776172fa3bf91a5476e564736f6c63430007060033" + }, + "c783df8a850f42e7F7e57013759C285caa701eB6": { "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }` // LocalParams are the params used for local networks LocalParams = Params{ @@ -59,24 +104,3 @@ var ( }, } ) - -func init() { - errs := wrappers.Errs{} - vmrqBytes, err := cb58.Decode(VMRQKeyStr) - errs.Add(err) - ewoqBytes, err := cb58.Decode(EWOQKeyStr) - errs.Add(err) - - factory := crypto.FactorySECP256K1R{} - vmrqIntf, err := factory.ToPrivateKey(vmrqBytes) - errs.Add(err) - ewoqIntf, err := factory.ToPrivateKey(ewoqBytes) - errs.Add(err) - - if errs.Err != nil { - panic(errs.Err) - } - - VMRQKey = vmrqIntf.(*crypto.PrivateKeySECP256K1R) - EWOQKey = ewoqIntf.(*crypto.PrivateKeySECP256K1R) -} diff --git a/avalanchego/genesis/genesis_songbird.go b/avalanchego/genesis/genesis_songbird.go new file mode 100644 index 00000000..946bd198 --- /dev/null +++ b/avalanchego/genesis/genesis_songbird.go @@ -0,0 +1,128 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package genesis + +import ( + "time" + + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" +) + +var ( + songbirdGenesisConfigJSON = `{ + "networkID": 5, + "allocations": [], + "startTime": 1626530000, + "initialStakeDuration": 31536000, + "initialStakeDurationOffset": 0, + "initialStakedFunds": [], + "initialStakers": [], + "cChainGenesis": "", + "message": "flare" + }` + + // songbirdChainGenesis is the C-Chain genesis block used for the Songbird + // canary network. + songbirdCChainGenesis = `{ + "config": { + "chainId": 19, + "homesteadBlock": 0, + "daoForkBlock": 0, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "apricotPhase1BlockTimestamp": 0, + "apricotPhase2BlockTimestamp": 0 + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x00", + "gasLimit": "0x5f5e100", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0100000000000000000000000000000000000000", + "alloc": { + "1000000000000000000000000000000000000001": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101735760003560e01c80637ec93e9f116100de578063c5d64cd111610097578063dccb2d3211610071578063dccb2d32146107f2578063ee2d87371461081e578063ef4c169e1461083b578063f9c490ee1461084357610173565b8063c5d64cd11461073e578063d3b92926146107b4578063d3fb3e9f146107d157610173565b80637ec93e9f146105a85780637f582432146105d45780638abd90ae1461069c5780638b203dd4146106a45780639c531f8c1461072e578063adc890321461073657610173565b806347453f371161013057806347453f37146104355780634bdc9c8f1461045257806355d14c15146104d857806371e8d61a14610504578063741d3cb41461057457806379fd4e1a1461057c57610173565b806307003bb41461017857806322ce7387146101945780632a2434a2146101cc5780632bec6f8714610247578063388492dd1461028f5780633f57987d14610403575b600080fd5b61018061084b565b604080519115158252519081900360200190f35b6101ba600480360360208110156101aa57600080fd5b50356001600160a01b0316610854565b60408051918252519081900360200190f35b6101ef600480360360208110156101e257600080fd5b503563ffffffff16610866565b60405180876001600160401b03168152602001866001600160401b031681526020018561ffff168152602001846001600160401b03168152602001838152602001828152602001965050505050505060405180910390f35b6102736004803603604081101561025d57600080fd5b506001600160a01b038135169060200135610a98565b604080516001600160401b039092168252519081900360200190f35b610357600480360360808110156102a557600080fd5b63ffffffff823516916020810135916001600160401b0360408301351691908101906080810160608201356401000000008111156102e257600080fd5b8201836020820111156102f457600080fd5b8035906020019184600183028401116401000000008311171561031657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610abe945050505050565b604051808663ffffffff168152602001856001600160401b03168152602001846001600160401b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b838110156103c45781810151838201526020016103ac565b50505050905090810190601f1680156103f15780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b6101806004803603604081101561041957600080fd5b50803563ffffffff1690602001356001600160401b03166114cb565b6102736004803603602081101561044b57600080fd5b50356115c7565b61046f6004803603602081101561046857600080fd5b5035611625565b604080519a15158b5260208b0199909952898901979097526060890195909552608088019390935260a08701919091526001600160401b0390811660c08701521660e085015215156101008401526001600160a01b031661012083015251908190036101400190f35b61046f600480360360408110156104ee57600080fd5b50803590602001356001600160401b031661169a565b610548600480360360a081101561051a57600080fd5b5063ffffffff813516906020810135906040810135906001600160401b036060820135169060800135611718565b604080516001600160401b03948516815292909316602083015215158183015290519081900360600190f35b6101ba61194b565b61046f6004803603604081101561059257600080fd5b50803590602001356001600160401b0316611951565b61046f600480360360408110156105be57600080fd5b506001600160a01b0381351690602001356119cf565b610357600480360360808110156105ea57600080fd5b63ffffffff823516916020810135916001600160401b03604083013516919081019060808101606082013564010000000081111561062757600080fd5b82018360208201111561063957600080fd5b8035906020019184600183028401116401000000008311171561065b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611a4c945050505050565b6101ba612460565b6106c7600480360360208110156106ba57600080fd5b503563ffffffff16612466565b604080519a15158b526001600160401b03998a1660208c01529789168a89015261ffff96871660608b015294909516608089015291861660a088015290941660c086015260e085019390935261010084015261012083019190915251908190036101400190f35b6102736124d6565b6101ba6124e5565b61077c6004803603608081101561075457600080fd5b5063ffffffff813516906001600160401b036020820135169060408101359060600135612588565b6040805163ffffffff90951685526001600160401b03909316602085015261ffff909116838301526060830152519081900360800190f35b610273600480360360208110156107ca57600080fd5b503561375d565b6107d9613778565b6040805163ffffffff9092168252519081900360200190f35b6102736004803603604081101561080857600080fd5b506001600160a01b038135169060200135613789565b61046f6004803603602081101561083457600080fd5b50356137fd565b610180613870565b6101ba614280565b60005460ff1681565b600d6020526000908152604090205481565b600080548190819081908190819060ff166108b25760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8716600090815260056020526040902054879060ff1661090c576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b600560008963ffffffff1663ffffffff168152602001908152602001600020600201549250600560008963ffffffff1663ffffffff168152602001908152602001600020600401549150600088600560008b63ffffffff1663ffffffff16815260200190815260200160002060000160159054906101000a90046001600160401b0316604051602001808363ffffffff1660e01b8152600401826001600160401b031660c01b81526008019250505060405160208183030381529060405280519060200120905060066000336001600160a01b03166001600160a01b03168152602001908152602001600020600082815260200190815260200160002060000160009054906101000a900460ff1615610a435733600090815260066020908152604080832084845290915281206004015490945092505b505063ffffffff969096166000908152600560205260409020805460019091015461010082046001600160401b0390811699600160a81b8404821699600160881b90940461ffff169850911695509193509150565b600b6020908152600092835260408084209091529082529020546001600160401b031681565b6000805481908190819060609060ff16610b095760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8916600090815260056020526040902054899060ff16610b63576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b88610baa576040805162461bcd60e51b815260206004820152601260248201527107061796d656e7448617368203d3d203078360741b604482015290519081900360640190fd5b63ffffffff8a166000908152600560205260409020600101546001600160401b0316610c075760405162461bcd60e51b81526004018080602001828103825260298152602001806144706029913960400191505060405180910390fd5b41331480610c18575041600160981b145b610c69576040805162461bcd60e51b815260206004820152601c60248201527f696e76616c696420626c6f636b2e636f696e626173652076616c756500000000604482015290519081900360640190fd5b6040805160e08c901b6001600160e01b03191660208083019190915260248083018d905283518084039091018152604490920183528151918101919091206000818152600a90925291902060060154600160801b900460ff1615610d0d576040805162461bcd60e51b81526020600482015260166024820152753830bcb6b2b73a1030b63932b0b23c90383937bb32b760511b604482015290519081900360640190fd5b63ffffffff8b166000908152600560205260409020600101546001600160401b03908116908a1610610d705760405162461bcd60e51b815260040180806020018281038252602e815260200180614442602e913960400191505060405180910390fd5b63ffffffff8b166000908152600560205260409020546001600160401b036101008204811691600160481b90041615610e515763ffffffff8c16600090815260056020526040902080546001909101546001600160401b03600160481b830481166101009093048116918116919091031611610e1d5760405162461bcd60e51b815260040180806020018281038252603981526020018061457e6039913960400191505060405180910390fd5b5063ffffffff8b16600090815260056020526040902080546001909101546001600160401b03600160481b90920482169116035b806001600160401b03168a6001600160401b03161015610eb8576040805162461bcd60e51b815260206004820152601a60248201527f6c6564676572203c20696e646578536561726368526567696f6e000000000000604482015290519081900360640190fd5b60008281526008602090815260408083206001600160401b038e16845290915281205460ff16156110415760008381526008602090815260408083206001600160401b038f168452909152902060040154421015610f475760405162461bcd60e51b81526004018080602001828103825260498152602001806143656049913960600191505060405180910390fd5b60008381526008602090815260408083206001600160401b038f1684529091529020600501548c14610fb6576040805162461bcd60e51b81526020600482015260136024820152720d2dcecc2d8d2c840e0c2f2dacadce890c2e6d606b1b604482015290519081900360640190fd5b426003546008600086815260200190815260200160002060008e6001600160401b03166001600160401b0316815260200190815260200160002060030154011161103c576040805162461bcd60e51b815260206004820152601260248201527172657665616c20697320746f6f206c61746560701b604482015290519081900360640190fd5b61107d565b413314801590611054575041600160981b145b1561107d575063ffffffff8c166000908152600560205260409020600101546001600160401b03165b4133148015611090575041600160981b14155b156114ba5760008381526008602090815260408083206001600160401b038f16845290915290205460ff16611269576040518061014001604052806001151581526020016000801b81526020016000801b8152602001428152602001600254420181526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600015158152602001336001600160a01b03168152506008600085815260200190815260200160002060008d6001600160401b03166001600160401b0316815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506114ba565b6040518061014001604052806001151581526020016000801b81526020016000801b8152602001600081526020014281526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600115158152602001336001600160a01b0316815250600a600085815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050507ff794f5b9b6791b3ab0eb97342cf2efbb7e710a478d4512a2505ea60c93fd85d38d8c8c8f33604051808663ffffffff168152602001856001600160401b0316815260200180602001848152602001836001600160a01b03168152602001828103825285818151815260200191508051906020019080838360005b8381101561147b578181015183820152602001611463565b50505050905090810190601f1680156114a85780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a15b9b9c999b9a99975050505050505050565b6000805460ff1661150d5760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8316600090815260056020526040902054839060ff16611567576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b50506040805160e09390931b6001600160e01b03191660208085019190915260c09290921b6001600160c01b03191660248401528051808403600c018152602c909301815282519282019290922060009081526007909152205460ff1690565b6000805460ff166116095760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b506000908152600c60205260409020546001600160401b031690565b60076020819052600091825260409091208054600182015460028301546003840154600485015460058601546006870154969097015460ff95861697949693959294919391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b60086020908152600092835260408084209091529082529020805460018201546002830154600384015460048501546005860154600687015460079097015460ff96871697959694959394929391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b600080548190819060ff1661175e5760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8816600090815260056020526040902054889060ff166117b8576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b604080516001600160401b0388166020808301919091528251808303820181528284018452805190820120606083018c9052608083018b905260a083015260c08083018990528351808403909101815260e08084018552815191830191909120908d901b6001600160e01b0319166101008401526101048084018290528451808503909101815261012490930184528251928201929092206000818152600a9092529290205490919060ff166118ae576040805162461bcd60e51b81526020600482015260166024820152751c185e5b595b9d08191bd95cc81b9bdd08195e1a5cdd60521b604482015290519081900360640190fd5b6000818152600a60205260409020600501548214611909576040805162461bcd60e51b81526020600482015260136024820152720d2dcecc2d8d2c840e0c2f2dacadce890c2e6d606b1b604482015290519081900360640190fd5b6000908152600a60205260409020600601546001600160401b038082169c600160401b83049091169b50600160801b90910460ff169950975050505050505050565b60025481565b60096020908152600092835260408084209091529082529020805460018201546002830154600384015460048501546005860154600687015460079097015460ff96871697959694959394929391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b600660208181526000938452604080852090915291835291208054600182015460028301546003840154600485015460058601549686015460079096015460ff95861697949693959294919391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b6000805481908190819060609060ff16611a975760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8916600090815260056020526040902054899060ff16611af1576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b88611b38576040805162461bcd60e51b815260206004820152601260248201527107061796d656e7448617368203d3d203078360741b604482015290519081900360640190fd5b63ffffffff8a166000908152600560205260409020600101546001600160401b0316611b955760405162461bcd60e51b81526004018080602001828103825260298152602001806144706029913960400191505060405180910390fd5b41331480611ba6575041600160981b145b611bf7576040805162461bcd60e51b815260206004820152601c60248201527f696e76616c696420626c6f636b2e636f696e626173652076616c756500000000604482015290519081900360640190fd5b6040805160e08c901b6001600160e01b03191660208083019190915260248083018d905283518084039091018152604490920183528151918101919091206000818152600a90925291902060060154600160801b900460ff1615611c98576040805162461bcd60e51b81526020600482015260136024820152723a3c24b21030b63932b0b23c90383937bb32b760691b604482015290519081900360640190fd5b6000818152600a60205260409020600601546001600160401b03808b16911610611cf35760405162461bcd60e51b815260040180806020018281038252602f8152602001806143ae602f913960400191505060405180910390fd5b63ffffffff8b166000908152600560205260409020600101546001600160401b03908116908a1610611d565760405162461bcd60e51b815260040180806020018281038252602e815260200180614442602e913960400191505060405180910390fd5b63ffffffff8b166000908152600560205260409020546001600160401b036101008204811691600160481b90041615611e375763ffffffff8c16600090815260056020526040902080546001909101546001600160401b03600160481b830481166101009093048116918116919091031611611e035760405162461bcd60e51b815260040180806020018281038252603981526020018061457e6039913960400191505060405180910390fd5b5063ffffffff8b16600090815260056020526040902080546001909101546001600160401b03600160481b90920482169116035b806001600160401b03168a6001600160401b03161015611e9e576040805162461bcd60e51b815260206004820152601a60248201527f6c6564676572203c20696e646578536561726368526567696f6e000000000000604482015290519081900360640190fd5b60008281526009602090815260408083206001600160401b038e16845290915281205460ff16156120275760008381526009602090815260408083206001600160401b038f168452909152902060040154421015611f2d5760405162461bcd60e51b81526004018080602001828103825260548152602001806142876054913960600191505060405180910390fd5b60008381526009602090815260408083206001600160401b038f1684529091529020600501548c14611f9c576040805162461bcd60e51b81526020600482015260136024820152720d2dcecc2d8d2c840e0c2f2dacadce890c2e6d606b1b604482015290519081900360640190fd5b426003546009600086815260200190815260200160002060008e6001600160401b03166001600160401b03168152602001908152602001600020600301540111612022576040805162461bcd60e51b815260206004820152601260248201527172657665616c20697320746f6f206c61746560701b604482015290519081900360640190fd5b612063565b41331480159061203a575041600160981b145b15612063575063ffffffff8c166000908152600560205260409020600101546001600160401b03165b4133148015612076575041600160981b14155b156114ba5760008381526009602090815260408083206001600160401b038f16845290915290205460ff1661224f576040518061014001604052806001151581526020016000801b81526020016000801b8152602001428152602001600254420181526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600015158152602001336001600160a01b03168152506009600085815260200190815260200160002060008d6001600160401b03166001600160401b0316815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506114ba565b6040518061014001604052806001151581526020016000801b81526020016000801b8152602001600081526020014281526020018d81526020018c6001600160401b03168152602001836001600160401b03168152602001600015158152602001336001600160a01b0316815250600a600085815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050507ff794f5b9b6791b3ab0eb97342cf2efbb7e710a478d4512a2505ea60c93fd85d38d8c8c8f33604051808663ffffffff168152602001856001600160401b0316815260200180602001848152602001836001600160a01b03168152602001828103825285818151815260200191508051906020019080838360008381101561147b578181015183820152602001611463565b60035481565b6005602052600090815260409020805460018201546002830154600384015460049094015460ff8416946001600160401b036101008604811695600160481b810482169561ffff600160881b8304811696600160981b840490911695600160a81b9093048416949190931692918a565b6004546001600160401b031681565b6000805460ff166125275760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b60015442116125675760405162461bcd60e51b81526004018080602001828103825260218152602001806145286021913960400191505060405180910390fd5b6004546001546001600160401b039091169042038161258257fe5b04905090565b6000805481908190819060ff166125d05760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b63ffffffff8816600090815260056020526040902054889060ff1661262a576040805162461bcd60e51b815260206004820152601660248201526000805160206145b7833981519152604482015290519081900360640190fd5b336000908152600d602052604090205442116126775760405162461bcd60e51b81526004018080602001828103825260458152602001806143dd6045913960600191505060405180910390fd5b866126b35760405162461bcd60e51b81526004018080602001828103825260218152602001806145d76021913960400191505060405180910390fd5b856126fb576040805162461bcd60e51b81526020600482015260136024820152720636861696e54697048617368203d3d2030783606c1b604482015290519081900360640190fd5b63ffffffff8916600090815260056020526040902054600160981b900461ffff166127575760405162461bcd60e51b81526004018080602001828103825260248152602001806144ca6024913960400191505060405180910390fd5b41331480612768575041600160981b145b6127b9576040805162461bcd60e51b815260206004820152601c60248201527f696e76616c696420626c6f636b2e636f696e626173652076616c756500000000604482015290519081900360640190fd5b63ffffffff891660009081526005602052604090208054600190910154600160881b90910461ffff166001600160401b0391821601811690891614612836576040805162461bcd60e51b815260206004820152600e60248201526d34b73b30b634b2103632b233b2b960911b604482015290519081900360640190fd5b63ffffffff8916600090815260056020526040902060020154421161288c5760405162461bcd60e51b81526004018080602001828103825260358152602001806145496035913960400191505060405180910390fd5b63ffffffff89166000908152600560205260409020600381015460049091015460020210156129215763ffffffff89166000908152600560205260409020600481015460029182015491024291909103600302101561291c5760405162461bcd60e51b815260040180806020018281038252602c815260200180614339602c913960400191505060405180910390fd5b612983565b63ffffffff8916600090815260056020526040902060048101546002909101544203600f0110156129835760405162461bcd60e51b815260040180806020018281038252602c815260200180614339602c913960400191505060405180910390fd5b63ffffffff8916600090815260056020908152604080832054815160e08e901b6001600160e01b03191681850152600160a81b90910460c01b6001600160c01b03191660248201528151808203600c018152602c9091018252805190830120808452600790925290912060060154600160801b900460ff1615612a4d576040805162461bcd60e51b815260206004820152601e60248201527f6c6f636174696f6e4861736820616c72656164792066696e616c697365640000604482015290519081900360640190fd5b63ffffffff8a16600090815260056020526040902054600160a81b90046001600160401b031615612b595760008a6001600560008e63ffffffff1663ffffffff16815260200190815260200160002060000160159054906101000a90046001600160401b031603604051602001808363ffffffff1660e01b8152600401826001600160401b031660c01b8152600801925050506040516020818303038152906040528051906020012090506007600082815260200190815260200160002060060160109054906101000a900460ff16612b575760405162461bcd60e51b81526004018080602001828103825260318152602001806144996031913960400191505060405180910390fd5b505b33600090815260066020908152604080832084845290915281205460ff1615612ce157336000908152600660209081526040808320858452909152902060040154421015612bd85760405162461bcd60e51b815260040180806020018281038252605e8152602001806142db605e913960600191505060405180910390fd5b6040805133606081901b60208084019190915260348084018d90528451808503909101815260549093018452825192810192909220600091825260068352838220868352909252919091206002015414612c70576040805162461bcd60e51b81526020600482015260146024820152730d2dcecc2d8d2c840c6d0c2d2dca8d2e090c2e6d60631b604482015290519081900360640190fd5b6003805433600090815260066020908152604080832087845290915290209091015442910111612cdc576040805162461bcd60e51b815260206004820152601260248201527172657665616c20697320746f6f206c61746560701b604482015290519081900360640190fd5b612d1c565b413314801590612cf4575041600160981b145b15612d1c575063ffffffff8a16600090815260056020526040902054600160981b900461ffff165b4133148015612d2f575041600160981b14155b156137485733600090815260066020908152604080832085845290915290205460ff16612f8b5763ffffffff8b1660009081526005602052604090206004015460028054910490811015612d8257506002545b6040518061014001604052806001151581526020018b81526020018a815260200142815260200182420181526020016000801b81526020018c6001600160401b0316815260200160006001600160401b0316815260200160001515815260200160006001600160a01b031681525060066000336001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506000805160206144228339815191528c8c600033604051808563ffffffff168152602001846001600160401b03168152602001836003811115612f6357fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a150613748565b63ffffffff8b16600090815260056020526040902054600160981b810461ffff16600160a81b9091046001600160401b0316111561335c5760008b600560008e63ffffffff1663ffffffff16815260200190815260200160002060000160139054906101000a900461ffff1661ffff16600560008f63ffffffff1663ffffffff16815260200190815260200160002060000160159054906101000a90046001600160401b031603604051602001808363ffffffff1660e01b8152600401826001600160401b031660c01b815260080192505050604051602081830303815290604052805190602001209050896007600083815260200190815260200160002060050154141561321e57600061309e6124e5565b90506001600b60006007600086815260200190815260200160002060070160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001908152602001600020600083815260200190815260200160002060008282829054906101000a90046001600160401b03160192506101000a8154816001600160401b0302191690836001600160401b031602179055506001600c600083815260200190815260200160002060008282829054906101000a90046001600160401b03160192506101000a8154816001600160401b0302191690836001600160401b031602179055506000805160206144228339815191528d8d60026007600087815260200190815260200160002060070160009054906101000a90046001600160a01b0316604051808563ffffffff168152602001846001600160401b031681526020018360038111156131f657fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a150613356565b600560008d63ffffffff1663ffffffff16815260200190815260200160002060030154600560008e63ffffffff1663ffffffff16815260200190815260200160002060000160139054906101000a900461ffff1661ffff16024201600d60006007600085815260200190815260200160002060070160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001908152602001600020819055506000805160206144228339815191528c8c60036007600086815260200190815260200160002060070160009054906101000a90046001600160a01b0316604051808563ffffffff168152602001846001600160401b0316815260200183600381111561333357fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a15b506133be565b6000805160206144228339815191528b8b600133604051808563ffffffff168152602001846001600160401b0316815260200183600381111561339b57fe5b8152602001826001600160a01b0316815260200194505050505060405180910390a15b6040518061014001604052806001151581526020018a81526020016000801b815260200160066000336001600160a01b03166001600160a01b0316815260200190815260200160002060008581526020019081526020016000206003015481526020014281526020018981526020018b6001600160401b0316815260200160006001600160401b03168152602001600115158152602001336001600160a01b03168152506007600084815260200190815260200160002060008201518160000160006101000a81548160ff0219169083151502179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160060160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160060160106101000a81548160ff0219169083151502179055506101208201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050506001600560008d63ffffffff1663ffffffff16815260200190815260200160002060000160158282829054906101000a90046001600160401b03160192506101000a8154816001600160401b0302191690836001600160401b0316021790555089600560008d63ffffffff1663ffffffff16815260200190815260200160002060010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060006002600560008e63ffffffff1663ffffffff16815260200190815260200160002060040154600560008f63ffffffff1663ffffffff1681526020019081526020016000206002015460066000336001600160a01b03166001600160a01b031681526020019081526020016000206000878152602001908152602001600020600301540301816136a757fe5b63ffffffff8e1660009081526005602052604090206003015491900491506002028111156136f75763ffffffff8c1660009081526005602052604090206003810154600202600490910155613713565b63ffffffff8c1660009081526005602052604090206004018190555b5033600090815260066020908152604080832085845282528083206003015463ffffffff8f1684526005909252909120600201555b999a6000199990990199989650505050505050565b600c602052600090815260409020546001600160401b031681565b600054610100900463ffffffff1681565b6000805460ff166137cb5760405162461bcd60e51b815260040180806020018281038252603a8152602001806144ee603a913960400191505060405180910390fd5b506001600160a01b03919091166000908152600b6020908152604080832093835292905220546001600160401b031690565b600a602052600090815260409020805460018201546002830154600384015460048501546005860154600687015460079097015460ff96871697959694959394929391926001600160401b0380841693600160401b810490911692600160801b909104909116906001600160a01b03168a565b6000805460ff16156138c0576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b604051806101400160405280600115158152602001620a84946001600160401b0316815260200160006001600160401b03168152602001600161ffff168152602001600461ffff16815260200160006001600160401b03168152602001620a84946001600160401b031681526020014281526020016103848152602001601e815250600560008063ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e0820151816002015561010082015181600301556101208201518160040155905050604051806101400160405280600115158152602001621fd4de6001600160401b0316815260200160006001600160401b03168152602001600161ffff168152602001600c61ffff16815260200160006001600160401b03168152602001621fd4de6001600160401b0316815260200142815260200160968152602001601e81525060056000600163ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e0820151816002015561010082015181600301556101208201518160040155905050604051806101400160405280600115158152602001623980b46001600160401b0316815260200160006001600160401b03168152602001600261ffff168152602001602861ffff16815260200160006001600160401b03168152602001623980b46001600160401b0316815260200142815260200160788152602001601e81525060056000600263ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160020155610100820151816003015561012082015181600401559050506040518061014001604052806001151581526020016303bf79006001600160401b0316815260200160006001600160401b03168152602001601e61ffff168152602001600161ffff16815260200160006001600160401b031681526020016303bf79006001600160401b0316815260200142815260200160788152602001601e81525060056000600363ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e082015181600201556101008201518160030155610120820151816004015590505060405180610140016040528060011515815260200162f6ba806001600160401b0316815260200160006001600160401b03168152602001601a61ffff168152602001600161ffff16815260200160006001600160401b0316815260200162f6ba806001600160401b0316815260200142815260200160788152602001601e81525060056000600463ffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a81548161ffff021916908361ffff16021790555060808201518160000160136101000a81548161ffff021916908361ffff16021790555060a08201518160000160156101000a8154816001600160401b0302191690836001600160401b0316021790555060c08201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160020155610100820151816003015561012082015181600401559050506005600060016101000a81548163ffffffff021916908363ffffffff160217905550601e6002819055506201518060038190555062093a80600460006101000a8154816001600160401b0302191690836001600160401b031602179055504260018190555060016000806101000a81548160ff0219169083151502179055506001905090565b6001548156fe626c6f636b2e74696d657374616d70203c2070726f706f7365644e6f6e5061796d656e7450726f6f66735b6c6f636174696f6e486173685d5b6c65646765725d2e7065726d697474656452657665616c54696d65626c6f636b2e74696d657374616d70203c2070726f706f73656444617461417661696c6162696c69747950726f6f66735b6d73672e73656e6465725d5b6c6f636174696f6e486173685d2e7065726d697474656452657665616c54696d656e6f7420656e6f7567682074696d6520656c61707365642073696e6365207072696f722066696e616c697479626c6f636b2e74696d657374616d70203c2070726f706f7365645061796d656e7450726f6f66735b6c6f636174696f6e486173685d2e7065726d697474656452657665616c54696d6566696e616c697365645061796d656e74735b6c6f636174696f6e486173685d2e696e646578203e3d206c65646765726d73672e73656e6465722069732063757272656e746c792062616e6e656420666f722073656e64696e6720616e20756e616363657074656420636861696e546970486173685d63f7a167c8b58acdc7fe63a4a0972032b67a76b50a2d5fd8d64061f44952316c6564676572203e3d20636861696e735b636861696e49645d2e66696e616c697365644c6564676572496e646578636861696e735b636861696e49645d2e66696e616c697365644c6564676572496e646578203d3d203070726576696f75732064617461417661696c6162696c697479506572696f64206e6f74207965742066696e616c69736564636861696e735b636861696e49645d2e6e756d436f6e6669726d6174696f6e73203e2030737461746520636f6e6e6563746f72206973206e6f7420696e697469616c697365642c2072756e20696e697469616c697365436861696e732829626c6f636b2e74696d657374616d70203c3d20696e697469616c69736554696d65626c6f636b2e74696d657374616d70203c3d20636861696e735b636861696e49645d2e66696e616c6973656454696d657374616d7066696e616c697365644c6564676572496e646578202d2067656e657369734c6564676572203c3d206c6564676572486973746f727953697a65636861696e496420646f6573206e6f742065786973740000000000000000000064617461417661696c6162696c697479506572696f6448617368203d3d20307830a26469706673582212209aec6c293dc2b1ff65eed9d71736d7da7ca3d806bfbd209f092ee91d3612309464736f6c63430007060033" + }, + "1000000000000000000000000000000000000002": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c80638be2fb8611610104578063d38bfff4116100a2578063e9de7d6011610071578063e9de7d6014610322578063ecdda0dd14610335578063ed21b6e414610348578063ee323c921461035f576101cf565b8063d38bfff4146102e9578063d48a38df146102fc578063dded1b4714610304578063e371aef01461030c576101cf565b8063a6817ace116100de578063a6817ace146102be578063be0522e0146102c6578063c373a08e146102ce578063c9f960eb146102e1576101cf565b80638be2fb861461029b5780638ccf77a0146102a35780639d6a890f146102ab576101cf565b806362da19a511610171578063689c49991161014b578063689c49991461026557806372993615146102785780637fec8d3814610280578063870196b814610288576101cf565b806362da19a51461023c578063639031431461024457806363d4a53a1461024c576101cf565b80635042916c116101ad5780635042916c146102025780635aa6e675146102175780635d36b1901461022c57806360f7ac9714610234576101cf565b806310663750146101d45780631d76dea1146101f25780634f6a77b5146101fa575b600080fd5b6101dc610372565b6040516101e991906127a1565b60405180910390f35b6101dc610378565b6101dc61037e565b61021561021036600461249c565b610384565b005b61021f6103da565b6040516101e99190612592565b6102156103e9565b61021f6104ab565b6101dc6104ba565b6101dc6104c0565b6102546104d0565b6040516101e9959493929190612698565b61021561027336600461240d565b61050b565b6101dc61083a565b6101dc610840565b61021561029636600461249c565b6108eb565b6101dc610a3b565b610215610a41565b6102156102b93660046123f1565b610a9c565b6101dc610aa1565b61021f610aa7565b6102156102dc3660046123f1565b610ab6565b61021f610b5b565b6102156102f73660046123f1565b610b9a565b6101dc610c5c565b6101dc610c62565b610314610c68565b6040516101e992919061277f565b61021561033036600461249c565b610c89565b6102546103433660046124b4565b610dc2565b61035061111f565b6040516101e993929190612623565b61021561036d3660046123f1565b6112e4565b60055481565b60095481565b60085481565b6000546001600160a01b031633146103d5576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600c55565b6000546001600160a01b031681565b6001546001600160a01b03163314610438576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6001546001600160a01b031681565b60045481565b60006104ca6113fb565b90505b90565b600b546060908190819081906000906104fa90600160c01b90046001600160401b03166001610dc2565b945094509450945094509091929394565b6000546001600160a01b0316331461055c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b604080518082019091526008815267746f6f206d616e7960c01b60208201528190600a8211156105a85760405162461bcd60e51b815260040161059f919061276c565b60405180910390fd5b506105b1611406565b60005b818110156108345760008484838181106105ca57fe5b6105e092602060409092020190810191506123f1565b6001600160a01b031614156040518060400160405280600c81526020016b61646472657373207a65726f60a01b8152509061062e5760405162461bcd60e51b815260040161059f919061276c565b5060105460005b818110156106d7576010818154811061064a57fe5b6000918252602090912001546001600160a01b031686868581811061066b57fe5b61068192602060409092020190810191506123f1565b6001600160a01b031614156040518060400160405280600b81526020016a647570206164647265737360a81b815250906106ce5760405162461bcd60e51b815260040161059f919061276c565b50600101610635565b5060108585848181106106e657fe5b6106fc92602060409092020190810191506123f1565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905584848381811061073a57fe5b905060400201602001356011600087878681811061075457fe5b61076a92602060409092020190810191506123f1565b6001600160a01b03166001600160a01b03168152602001908152602001600020819055506000601260008787868181106107a057fe5b6107b692602060409092020190810191506123f1565b6001600160a01b031681526020810191909152604001600020557f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb598585848181106107fd57fe5b61081392602060409092020190810191506123f1565b6001604051610823929190612737565b60405180910390a1506001016105b4565b50505050565b60075481565b6002546000906001600160a01b031661088557610885306040518060400160405280600e81526020016d696e666c6174696f6e207a65726f60901b81525060006114b6565b60026001609c1b01331461089857600080fd5b6108a061167a565b905060006108ac611ee5565b90504781146108e7576108e7306040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b81525060006114b6565b5090565b6000546001600160a01b0316331461093c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60085461094c90606e6064611f0a565b811115604051806040016040528060118152602001700dac2f040dad2dce840e8dede40d0d2ced607b1b815250906109975760405162461bcd60e51b815260040161059f919061276c565b5060408051808201909152601081526f6d6178206d696e74206973207a65726f60801b6020820152816109dd5760405162461bcd60e51b815260040161059f919061276c565b5062015180600a54014211604051806040016040528060128152602001711d1a5b594819d85c081d1bdbc81cda1bdc9d60721b81525090610a315760405162461bcd60e51b815260040161059f919061276c565b5060085542600a55565b60035481565b6000546001600160a01b03163314610a92576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b610a9a611406565b565bfe5b50565b600a5481565b6002546001600160a01b031681565b6000546001600160a01b03163314610b07576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b600f5460009060ff16610b8857600f805460ff191660011790556000610b7f612012565b91506104cd9050565b506000546001600160a01b03166104cd565b6000546001600160a01b03163314610beb576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b600c5481565b60065481565b600b546001600160c01b03811690600160c01b90046001600160401b031682565b60025460408051808201909152600d81526c3737ba1034b7333630ba34b7b760991b602082015233916001600160a01b03168214610cda5760405162461bcd60e51b815260040161059f919061276c565b5060085482111560405180604001604052806007815260200166746f6f2062696760c81b81525090610d1f5760405162461bcd60e51b815260040161059f919061276c565b5042610d296113fb565b10604051806040016040528060098152602001683a37b79037b33a32b760b91b81525090610d6a5760405162461bcd60e51b815260040161059f919061276c565b508115610dbe5742600955600454610d829083612032565b6004556040517f4c4f1efc376f31abeb51b72c5f9ed81cf4016591312bb02337e58149dcfaaab490610db59084906127a1565b60405180910390a15b5050565b606080606080600060148054905087106040518060400160405280601081526020016f0e6e8c2e4e840d2dcc8caf040d0d2ced60831b81525090610e195760405162461bcd60e51b815260040161059f919061276c565b506014546000908888011115610e3457601454889003610e36565b865b9050806001600160401b0381118015610e4e57600080fd5b50604051908082528060200260200182016040528015610e78578160200160208202803683370190505b509550806001600160401b0381118015610e9157600080fd5b50604051908082528060200260200182016040528015610ebb578160200160208202803683370190505b509450806001600160401b0381118015610ed457600080fd5b50604051908082528060200260200182016040528015610f0857816020015b6060815260200190600190039081610ef35790505b509350806001600160401b0381118015610f2157600080fd5b50604051908082528060200260200182016040528015610f4b578160200160208202803683370190505b50925060005b818110156111035760006014828b0181548110610f6a57fe5b6000918252602080832090910154808352601390915260409091205489519192506001600160c01b031690899084908110610fa157fe5b6020026020010181815250506013600082815260200190815260200160002060000160189054906101000a90046001600160401b03166001600160401b0316878381518110610fec57fe5b6020908102919091018101919091526000828152601382526040908190206002908101805483516001821615610100026000190190911692909204601f810185900485028301850190935282825290929091908301828280156110905780601f1061106557610100808354040283529160200191611090565b820191906000526020600020905b81548152906001019060200180831161107357829003601f168201915b50505050508683815181106110a157fe5b60200260200101819052506013600082815260200190815260200160002060010160009054906101000a90046001600160a01b03168583815181106110e257fe5b6001600160a01b039092166020928302919091019091015250600101610f51565b5050600b549497939650919450926001600160c01b0316919050565b60105460609081908190806001600160401b038111801561113f57600080fd5b50604051908082528060200260200182016040528015611169578160200160208202803683370190505b509350806001600160401b038111801561118257600080fd5b506040519080825280602002602001820160405280156111ac578160200160208202803683370190505b509250806001600160401b03811180156111c557600080fd5b506040519080825280602002602001820160405280156111ef578160200160208202803683370190505b50915060005b818110156112dd5760006010828154811061120c57fe5b9060005260206000200160009054906101000a90046001600160a01b031690508086838151811061123957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060116000826001600160a01b03166001600160a01b031681526020019081526020016000205485838151811061128b57fe5b60200260200101818152505060126000826001600160a01b03166001600160a01b03168152602001908152602001600020548483815181106112c957fe5b6020908102919091010152506001016111f5565b5050909192565b6000546001600160a01b03163314611335576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60408051808201909152600e81526d696e666c6174696f6e207a65726f60901b60208201526001600160a01b0382166113815760405162461bcd60e51b815260040161059f919061276c565b506002546040517f4bdd1012a7d55ed9afad8675a125e1b68c7c15f712c0f3d5cddac69c3b979805916113bf9184916001600160a01b031690612752565b60405180910390a1600280546001600160a01b0319166001600160a01b038316179055600854610a9e576a295be96e6406697200000060085550565b600954620143700190565b60105460005b81811015610dbe576010805460009190600019810190811061142a57fe5b600091825260209091200154601080546001600160a01b039092169250908061144f57fe5b600082815260208120820160001990810180546001600160a01b03191690559091019091556040517f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb59916114a591849190612737565b60405180910390a15060010161140c565b600083836040516020016114cb9291906125a6565b60408051808303601f190181529181528151602092830120600081815260139093529120805491925090600160c01b90046001600160401b03166115b25760148054600180820183556000929092527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec01839055810180546001600160a01b0319166001600160a01b038716179055611565846040612095565b805161157b916002840191602090910190612359565b5060145460018201805467ffffffffffffffff60a01b1916600160a01b6000199093016001600160401b0316929092029190911790555b8054436001600160c01b038181166001600160401b03600160c01b80860482166001019091160291909316176001600160c01b0319169190911782556040517f7a459ed083a9b267865360013a5ad6dbc07e5befe6e4f71671c940fdd4206bee9161162391889190889088906125eb565b60405180910390a1600b80546001600160c01b0319811660016001600160c01b0392831681018316919091178084559301549216600160a01b9092046001600160401b0316600160c01b0291909117905550505050565b600060035443141561168e575060006104cd565b43600355600d544790811115611a645760006116b7600e54600d5461203290919063ffffffff16565b90508082141561182c57600e546005546116d19082612032565b6005556040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c906117049083906127a1565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561175c57600080fd5b505af19350505050801561176e575060015b6117dd5761177a6127b0565b806117855750611797565b611791308260006114b6565b506117d8565b6117d8306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006114b6565b611826565b6006546117ea9082612032565b6006556040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a9061181d9083906127a1565b60405180910390a15b50611a62565b8082101561188e57600061184b600d548461214990919063ffffffff16565b60075490915061185b9082612032565b6007556040517f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab3689061181d9083906127a1565b600e5460055461189d91612032565b600555600e54600d546000916118be916118b8908690612149565b90612149565b6007549091506118ce9082612032565b600755600e546040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c91611902916127a1565b60405180910390a17f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab3688160405161193991906127a1565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5600e546040518263ffffffff1660e01b81526004016000604051808303818588803b15801561199357600080fd5b505af1935050505080156119a5575060015b611a14576119b16127b0565b806119bc57506119ce565b6119c8308260006114b6565b50611a0f565b611a0f306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006114b6565b611a60565b600e54600654611a2391612032565b600655600e546040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a91611a57916127a1565b60405180910390a15b505b505b60105460005b81811015611e8357600060108281548110611a8157fe5b60009182526020808320909101546001600160a01b031680835260129091526040909120549091508015611b0d576001600160a01b0382166000908152601260205260409081902060001983019055517f9895eddb1e8569b1dae526135aa5cab97f982fdc3b0ff7e17920c95e3b9bda6290611b0090849084906125d2565b60405180910390a1611e79565b6001600160a01b038216600090815260116020526040812054905a90506204a768811015611b77577f9b5c4be38598cb8d8b6e07727d2303d1d9fc2dfc31ad323170f5ea4dcc1f914a858703604051611b6691906127a1565b60405180910390a150505050611e83565b620493df1981018215801590611b8c57508083105b15611b945750815b846001600160a01b0316636d0e8c34826040518263ffffffff1660e01b8152600401602060405180830381600088803b158015611bd057600080fd5b5087f193505050508015611c01575060408051601f3d908101601f19168201909252611bfe9181019061247c565b60015b611e3857611c0d6127b0565b80611c185750611c9f565b611c2586825a86036114b6565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611c6057600080fd5b505af1158015611c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c98919061247c565b5050611e33565b60005a9050600084118015611cbd575083611cba8483612149565b10155b15611d9057611cf1866040518060400160405280600a8152602001696f7574206f662067617360b01b8152508386036114b6565b6000866001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611d2e57600080fd5b505af1158015611d42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d66919061247c565b905080611d8a57600c546001600160a01b0388166000908152601260205260409020555b50611e31565b611dbc86604051806040016040528060078152602001663ab735b737bbb760c91b8152508386036114b6565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611df757600080fd5b505af1158015611e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e2f919061247c565b505b505b611e75565b507fe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4855a8403604051611e6c9291906125d2565b60405180910390a15b5050505b5050600101611a6a565b50611e8c6121a6565b92508215611ed657600e8390556040517f34f843cef0df42035141347873da1758a6643358831b5ba5b1580be947644f9290611ec99085906127a1565b60405180910390a1611edc565b6000600e555b505047600d5590565b60006104ca600754611f0460065460055461214990919063ffffffff16565b90612032565b6000808211611f53576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b83611f605750600061200b565b83830283858281611f6d57fe5b041415611f8657828181611f7d57fe5b0491505061200b565b6000838681611f9157fe5b0490506000848781611f9f57fe5b0690506000858781611fad57fe5b0490506000868881611fbb57fe5b069050612003611fd588611fcf86856121bf565b90612218565b611f04611fe286866121bf565b611f04611fef89876121bf565b611f048d611ffd8c8b6121bf565b906121bf565b955050505050505b9392505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76104ca8161227f565b60008282018381101561208c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60606000839050828151116120ad578391505061208f565b6000836001600160401b03811180156120c557600080fd5b506040519080825280601f01601f1916602001820160405280156120f0576020820181803683370190505b50905060005b848110156121405782818151811061210a57fe5b602001015160f81c60f81b82828151811061212157fe5b60200101906001600160f81b031916908160001a9053506001016120f6565b50949350505050565b6000828211156121a0576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006104ca60055460045461214990919063ffffffff16565b6000826121ce5750600061208f565b828202828482816121db57fe5b041461208c5760405162461bcd60e51b815260040180806020018281038252602181526020018061286a6021913960400191505060405180910390fd5b600080821161226e576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161227757fe5b049392505050565b600154600160a01b900460ff16156122d5576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261238f57600085556123d5565b82601f106123a857805160ff19168380011785556123d5565b828001600101855582156123d5579182015b828111156123d55782518255916020019190600101906123ba565b506108e79291505b808211156108e757600081556001016123dd565b600060208284031215612402578081fd5b813561208c81612854565b6000806020838503121561241f578081fd5b82356001600160401b0380821115612435578283fd5b818501915085601f830112612448578283fd5b813581811115612456578384fd5b86602060408302850101111561246a578384fd5b60209290920196919550909350505050565b60006020828403121561248d578081fd5b8151801515811461208c578182fd5b6000602082840312156124ad578081fd5b5035919050565b600080604083850312156124c6578182fd5b50508035926020909101359150565b6000815180845260208085019450808401835b8381101561250d5781516001600160a01b0316875295820195908201906001016124e8565b509495945050505050565b6000815180845260208085019450808401835b8381101561250d5781518752958201959082019060010161252b565b60008151808452815b8181101561256c57602081850181015186830182015201612550565b8181111561257d5782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03831681526040602082018190526000906125ca90830184612547565b949350505050565b6001600160a01b03929092168252602082015260400190565b600060018060a01b0386168252846020830152608060408301526126126080830185612547565b905082606083015295945050505050565b606080825284519082018190526000906020906080840190828801845b828110156126655781516001600160a01b031684529284019290840190600101612640565b505050838103828501526126798187612518565b915050828103604084015261268e8185612518565b9695505050505050565b600060a082526126ab60a0830188612518565b6020838203818501526126be8289612518565b848103604086015287518082529092508183019082810284018301838a01865b8381101561270c57601f198784030185526126fa838351612547565b948601949250908501906001016126de565b50508681036060880152612720818a6124d5565b955050505050508260808301529695505050505050565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b0392831681529116602082015260400190565b60006020825261200b6020830184612547565b6001600160c01b039290921682526001600160401b0316602082015260400190565b90815260200190565b60e01c90565b600060443d10156127c0576104cd565b600481823e6308c379a06127d482516127aa565b146127de576104cd565b6040513d600319016004823e80513d6001600160401b03816024840111818411171561280d57505050506104cd565b8284019250825191508082111561282757505050506104cd565b503d8301602082840101111561283f575050506104cd565b601f01601f1916810160200160405291505090565b6001600160a01b0381168114610a9e57600080fdfe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a26469706673582212204565821f65608e6e4fdb7076f78e5f829e603a82de85005cd73fca61d73e634f64736f6c63430007060033" + }, + "1000000000000000000000000000000000000003": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c80639d6a890f116100a2578063c373a08e11610071578063c373a08e1461051b578063c5adc53914610541578063c9f960eb1461066b578063d38bfff414610673578063ffacb84e1461069957610116565b80639d6a890f146104205780639d986f91146104465780639ec2b58114610472578063b39c68581461051357610116565b806371e1fad9116100e957806371e1fad9146102fd57806376794efb146103055780637ac420ad146103a85780638ab63380146103e05780638c9d28b61461041857610116565b80635aa6e6751461011b5780635d36b1901461013f57806360848b441461014957806360f7ac97146102f5575b600080fd5b6101236106f1565b604080516001600160a01b039092168252519081900360200190f35b610147610700565b005b6101476004803603608081101561015f57600080fd5b81359190810190604081016020820135600160201b81111561018057600080fd5b82018360208201111561019257600080fd5b803590602001918460208302840111600160201b831117156101b357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561020257600080fd5b82018360208201111561021457600080fd5b803590602001918460208302840111600160201b8311171561023557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561028457600080fd5b82018360208201111561029657600080fd5b803590602001918460208302840111600160201b831117156102b757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506107c2945050505050565b610123610dbe565b610123610dcd565b6101476004803603604081101561031b57600080fd5b810190602081018135600160201b81111561033557600080fd5b82018360208201111561034757600080fd5b803590602001918460208302840111600160201b8311171561036857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610ddc915050565b6103ce600480360360208110156103be57600080fd5b50356001600160a01b0316610ebd565b60408051918252519081900360200190f35b610147600480360360608110156103f657600080fd5b506001600160a01b038135811691602081013582169160409091013516610ed8565b610123610f68565b6101476004803603602081101561043657600080fd5b50356001600160a01b0316610f77565b6101476004803603604081101561045c57600080fd5b506001600160a01b038135169060200135610f79565b6101476004803603602081101561048857600080fd5b810190602081018135600160201b8111156104a257600080fd5b8201836020820111156104b457600080fd5b803590602001918460208302840111600160201b831117156104d557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611029945050505050565b610123611177565b6101476004803603602081101561053157600080fd5b50356001600160a01b0316611186565b6101476004803603606081101561055757600080fd5b81359190810190604081016020820135600160201b81111561057857600080fd5b82018360208201111561058a57600080fd5b803590602001918460208302840111600160201b831117156105ab57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156105fa57600080fd5b82018360208201111561060c57600080fd5b803590602001918460208302840111600160201b8311171561062d57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061122b945050505050565b6101236115e6565b6101476004803603602081101561068957600080fd5b50356001600160a01b031661160b565b6106a16116cd565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156106dd5781810151838201526020016106c5565b505050509050019250505060405180910390f35b6000546001600160a01b031681565b6001546001600160a01b0316331461074f576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b8251825160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146108825760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561084757818101518382015260200161082f565b50505050905090810190601f1680156108745780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50815181146040518060400160405280601a815260200179082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b815250906109065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528751602484015287516000946001600160a01b031693639cb47538938a939283926044019180860191028083838b5b8381101561096757818101518382015260200161094f565b505050509050019250505060006040518083038186803b15801561098a57600080fd5b505afa15801561099e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156109c757600080fd5b8101908080516040519392919084600160201b8211156109e657600080fd5b9083019060208201858111156109fb57600080fd5b82518660208202830111600160201b82111715610a1757600080fd5b82525081516020918201928201910280838360005b83811015610a44578181015183820152602001610a2c565b5050505091909101604090815233600090815260056020529081205495965093506000199250839150505b85811015610c9e576000898281518110610a8557fe5b60200260200101519050806001901b851660001415610b2e5783610b2e573360009081526007602052604090205460ff1615610ac45760019350610b2e565b604080518082018252600f81526e139bdd081dda1a5d195b1a5cdd1959608a1b6020808301918252925162461bcd60e51b8152600481019384528251602482015282519293928392604490920191908083836000831561084757818101518382015260200161082f565b600019831415610bd157858281518110610b4457fe5b60200260200101516001600160a01b031663f72cab28338d6040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ba257600080fd5b505af1158015610bb6573d6000803e3d6000fd5b505050506040513d6020811015610bcc57600080fd5b505192505b858281518110610bdd57fe5b60200260200101516001600160a01b03166355f7b69b338d8c8681518110610c0157fe5b60200260200101518c8781518110610c1557fe5b6020026020010151886040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182815260200195505050505050600060405180830381600087803b158015610c7957600080fd5b505af1158015610c8d573d6000803e3d6000fd5b505060019093019250610a6f915050565b5088336001600160a01b03167fa32444a31df2f9a116229eec3193d223a6bad89f7670ff17b8e5c7014a377da1868a8a4260405180806020018060200180602001858152602001848103845288818151815260200191508051906020019060200280838360005b83811015610d1d578181015183820152602001610d05565b50505050905001848103835287818151815260200191508051906020019060200280838360005b83811015610d5c578181015183820152602001610d44565b50505050905001848103825286818151815260200191508051906020019060200280838360005b83811015610d9b578181015183820152602001610d83565b5050505090500197505050505050505060405180910390a3505050505050505050565b6001546001600160a01b031681565b6004546001600160a01b031690565b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b03163314610e635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060005b8251811015610eb857816001901b1960056000858481518110610e8657fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002080549091169055600101610e67565b505050565b6001600160a01b031660009081526005602052604090205490565b6000546001600160a01b03163314610f29576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600280546001600160a01b039485166001600160a01b031991821617909155600480549385169382169390931790925560038054919093169116179055565b6002546001600160a01b031690565bfe5b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b031633146110005760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506001600160a01b0390911660009081526005602052604090208054600190921b919091179055565b60035460408051808201909152601081526f4654534f4d616e61676572206f6e6c7960801b6020820152906001600160a01b031633146110aa5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b5060065460005b8181101561110857600060076000600684815481106110cc57fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff19169115159190911790556001016110b1565b5050805160005b818110156111635760016007600085848151811061112957fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905560010161110f565b508151610eb8906006906020850190611809565b6003546001600160a01b031690565b6000546001600160a01b031633146111d7576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b8151815160408051808201909152601a815279082e4e4c2f240d8cadccee8d0e640c8de40dcdee840dac2e8c6d60331b60208201529082146112ae5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561084757818101518382015260200161082f565b506002546040516313968ea760e31b81526020600482018181528651602484015286516000946001600160a01b031693639cb475389389939283926044019180860191028083838b5b8381101561130f5781810151838201526020016112f7565b505050509050019250505060006040518083038186803b15801561133257600080fd5b505afa158015611346573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561136f57600080fd5b8101908080516040519392919084600160201b82111561138e57600080fd5b9083019060208201858111156113a357600080fd5b82518660208202830111600160201b821117156113bf57600080fd5b82525081516020918201928201910280838360005b838110156113ec5781810151838201526020016113d4565b50505050919091016040908152336000908152600560205290812054959650935083925050505b8481101561150e57600087828151811061142957fe5b60200260200101519050806001901b8416600014156114645782611464573360009081526007602052604090205460ff1615610ac457600192505b84828151811061147057fe5b60200260200101516001600160a01b03166327bd2ad5338b8a868151811061149457fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050600060405180830381600087803b1580156114e957600080fd5b505af11580156114fd573d6000803e3d6000fd5b505060019093019250611413915050565b5086336001600160a01b03167f90c022ade239639b1f8c4ebb8a76df5e03a7129df46cf9bcdae3c1450ea35434858842604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015611588578181015183820152602001611570565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156115c75781810151838201526020016115af565b505050509050019550505050505060405180910390a350505050505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76116068161172f565b905090565b6000546001600160a01b0316331461165c576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6060600680548060200260200160405190810160405280929190818152602001828054801561172557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611707575b5050505050905090565b600154600160a01b900460ff1615611785576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b82805482825590600052602060002090810192821561185e579160200282015b8281111561185e57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611829565b5061186a92915061186e565b5090565b5b8082111561186a576000815560010161186f56fea2646970667358221220c1d73dd5d713527b4b18f6ca6d3d915fe356fa5425b5776172fa3bf91a5476e564736f6c63430007060033" + }, + "a7235ad1532c178603B457E9671e32eD61ed8372": {"balance": "0x086e4892512842830694c140"}, + "6699E078e90d646E02C2a2976a738C34dC7bF0Bf": {"balance": "0x086e4892512842830694c140"}, + "Fb6148ea2c87F424e8F3DED3567cfd7b179C1b6f": {"balance": "0x01390300fa822e2d37588e80"}, + "780B144e034341a970c2e4F93f2f6Cd82E8DFadc": {"balance": "0x018b7ee82f9c7d5052200000"}, + "d197F63CEefDaFaAF771a722c0A9cce771912b41": {"balance": "0x0292cf5547755cbe4101e000"}, + "10fc0865d11eebc61eA23511183305EE18b77939": {"balance": "0x0292cf5547755cbe4101e000"}, + "dde49343a309941ca44ea257bA3c897B47B6b1D4": {"balance": "0x0292cf5547755cbe4101e000"}, + "8078637A052E39A2Fc8833878D70Ae41f7310290": {"balance": "0x0292cf5547755cbe4101e000"}, + "C6Da0f9C2c2542cC8860f60d6b160ce6573C5F60": {"balance": "0x01363156bbee3016d7000000"}, + "b2718a5EB8C68399326eD4c358A26cB1DDAE27e1": {"balance": "0x01363156bbee3016d7000000"}, + "806dfa5ceeAA6249B1ab038eBd01Df315dd45485": {"balance": "0x4dc8025ee967a73afec000"}, + "7B3283826833D9E204C3c7cbD39feF31227C9868": {"balance": "0x4dc8025ee967a73afec000"}, + "0f71a5aa9B97A4a186604583CBbFfBAb3bd4dE0C": {"balance": "0x4dc8025ee967a73afec000"}, + "BEb5Aafa635ec3D1C393f2bB599383bB3C854Bdc": {"balance": "0x4dc8025ee967a73afec000"}, + "e9B8ED5c3cF88415DBe50FaF94F0b0302d4c329e": {"balance": "0x4dc8025ee967a73afec000"}, + "54a5F325103D8113686C62FF73C3251E064247bB": {"balance": "0x4dc8025ee967a73afec000"}, + "4190123e3bf34276d601B877dC4CeFaB3bbE667f": {"balance": "0x25391ee35a05c54d000000"}, + "706db31fdbd1D13B2678826c5d30C20a0B5AE097": {"balance": "0x25391ee35a05c54d000000"}, + "aE4644F8E49B031b8170Ec6C9bB0C24501A69C2f": {"balance": "0x25391ee35a05c54d000000"}, + "d5E575E2B1F61Fd5e9F8D828feb8F53C554Eb998": {"balance": "0x06342fd08f00f637800000"}, + "f763dd0bB23e580097f69D17e2a60b04EB605147": {"balance": "0x1f04ef12cb04cf15800000"}, + "009A4624646501fe3cdE44B8CD1A435D81aA9186": {"balance": "0x1f04ef12cb04cf15800000"}, + "E5D4F5a40097128b826bE833c7c02F47a8D99a55": {"balance": "0x06342fd08f00f637800000"}, + "E8c27Ec6A03612e21c42f5616891b6FFC416b1D2": {"balance": "0x1f04ef12cb04cf15800000"}, + "c5DAE0d309EFa1C5D5C90bBa773Dc9e9176fE956": {"balance": "0x1f04ef12cb04cf15800000"}, + "040dd8bD2F7e1ad893eAa4d0837fE210373fF190": {"balance": "0x35c4490f820855e1000000"}, + "399Ff6428D4e616BE8ce21Ee5beC9d344185e8D9": {"balance": "0x2116545850052128000000"}, + "489506707A68bEdAd0B62c57e3226506b54a9364": {"balance": "0x2116545850052128000000"}, + "4AC175dcf8355A5Ed545a7178715c542cF43B9bB": {"balance": "0x2116545850052128000000"}, + "544DF305ef3ef012108D770B259720E7Ef6360Bd": {"balance": "0x2116545850052128000000"}, + "42a7bD36593c75c981C7201d2E7974133782f0e0": {"balance": "0x0c9a0d0a69d549fff26def00"}, + "493044fbBAA7F9F78379864fA88aCcaFf6A7586e": {"balance": "0x5150ae84a8cdf00000"} + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }` + + // SongbirdParams are the params used for the Songbird canary network. + SongbirdParams = Params{ + TxFeeConfig: TxFeeConfig{ + TxFee: units.MilliAvax, + CreateAssetTxFee: 10 * units.MilliAvax, + CreateSubnetTxFee: 1 * units.Avax, + CreateBlockchainTxFee: 1 * units.Avax, + }, + StakingConfig: StakingConfig{ + UptimeRequirement: .8, // 80% + MinValidatorStake: 2 * units.KiloAvax, + MaxValidatorStake: 3 * units.MegaAvax, + MinDelegatorStake: 25 * units.Avax, + MinDelegationFee: 20000, // 2% + MinStakeDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + RewardConfig: reward.Config{ + MaxConsumptionRate: .12 * reward.PercentDenominator, + MinConsumptionRate: .10 * reward.PercentDenominator, + MintingPeriod: 365 * 24 * time.Hour, + SupplyCap: 720 * units.MegaAvax, + }, + }, + } +) diff --git a/avalanchego/genesis/genesis_test.go b/avalanchego/genesis/genesis_test.go index 48b15ee8..1cb9bdfd 100644 --- a/avalanchego/genesis/genesis_test.go +++ b/avalanchego/genesis/genesis_test.go @@ -178,7 +178,7 @@ func TestGenesisFromFile(t *testing.T) { }, "fuji (with custom specified)": { networkID: constants.FujiID, - customConfig: localGenesisConfigJSON, // won't load + customConfig: []byte(localGenesisConfigJSON), // won't load err: "cannot override genesis config for standard network fuji (5)", }, "local": { @@ -198,7 +198,7 @@ func TestGenesisFromFile(t *testing.T) { }, "custom (networkID mismatch)": { networkID: 9999, - customConfig: localGenesisConfigJSON, + customConfig: []byte(localGenesisConfigJSON), err: "networkID 9999 specified but genesis config contains networkID 12345", }, "custom (invalid format)": { @@ -276,7 +276,7 @@ func TestGenesisFromFlag(t *testing.T) { }, "custom (networkID mismatch)": { networkID: 9999, - customConfig: localGenesisConfigJSON, + customConfig: []byte(localGenesisConfigJSON), err: "networkID 9999 specified but genesis config contains networkID 12345", }, "custom (invalid format)": { diff --git a/avalanchego/genesis/params.go b/avalanchego/genesis/params.go index f9be8500..b16ac76a 100644 --- a/avalanchego/genesis/params.go +++ b/avalanchego/genesis/params.go @@ -53,8 +53,9 @@ func GetTxFeeConfig(networkID uint32) TxFeeConfig { switch networkID { case constants.MainnetID: return MainnetParams.TxFeeConfig - case constants.FujiID: - return FujiParams.TxFeeConfig + // SGB-MERGE + // case constants.FujiID: + // return FujiParams.TxFeeConfig case constants.LocalID: return LocalParams.TxFeeConfig case constants.FlareID: @@ -65,6 +66,10 @@ func GetTxFeeConfig(networkID uint32) TxFeeConfig { return StagingParams.TxFeeConfig case constants.LocalFlareID: return LocalFlareParams.TxFeeConfig + case constants.SongbirdID: + return SongbirdParams.TxFeeConfig + case constants.CostonID: + return CostonParams.TxFeeConfig default: return LocalParams.TxFeeConfig } @@ -74,8 +79,9 @@ func GetStakingConfig(networkID uint32) StakingConfig { switch networkID { case constants.MainnetID: return MainnetParams.StakingConfig - case constants.FujiID: - return FujiParams.StakingConfig + // SGB-MERGE + // case constants.FujiID: + // return FujiParams.StakingConfig case constants.LocalID: return LocalParams.StakingConfig case constants.FlareID: @@ -86,6 +92,10 @@ func GetStakingConfig(networkID uint32) StakingConfig { return StagingParams.StakingConfig case constants.LocalFlareID: return LocalFlareParams.StakingConfig + case constants.SongbirdID: + return SongbirdParams.StakingConfig + case constants.CostonID: + return CostonParams.StakingConfig default: return LocalParams.StakingConfig } diff --git a/avalanchego/node/node.go b/avalanchego/node/node.go index 33690f1c..319a473e 100644 --- a/avalanchego/node/node.go +++ b/avalanchego/node/node.go @@ -727,7 +727,7 @@ func (n *Node) initVMs() error { // Instead of updating node's validator manager, platform chain makes changes // to its own local validator manager (which isn't used for sampling) if !n.Config.EnableStaking { - vdrs = validators.NewManager() + vdrs = validators.NewManager(n.Config.NetworkID) } vmRegisterer := registry.NewVMRegisterer(registry.VMRegistererConfig{ @@ -1120,7 +1120,7 @@ func (n *Node) initAPIAliases(genesisBytes []byte) error { // Initializes [n.vdrs] and returns the Primary Network validator set. func (n *Node) initVdrs() (validators.Set, error) { - n.vdrs = validators.NewManager() + n.vdrs = validators.NewManager(n.Config.NetworkID) vdrSet := validators.NewSet() if err := n.vdrs.Set(constants.PrimaryNetworkID, vdrSet); err != nil { return vdrSet, fmt.Errorf("couldn't set primary network validators: %w", err) diff --git a/avalanchego/scripts/keys/6b0dd034a2fd67b932f10e3dba1d2bbd39348695.json b/avalanchego/scripts/keys/6b0dd034a2fd67b932f10e3dba1d2bbd39348695.json new file mode 100644 index 00000000..3dbd41c5 --- /dev/null +++ b/avalanchego/scripts/keys/6b0dd034a2fd67b932f10e3dba1d2bbd39348695.json @@ -0,0 +1 @@ +{"address":"6b0dd034a2fd67b932f10e3dba1d2bbd39348695","crypto":{"cipher":"aes-128-ctr","ciphertext":"ab55ba12d70fc71b75b68ba663faab2c5c292371f1daff89ebc6a0bd621005f8","cipherparams":{"iv":"534f560a84f45f53cfd5c1c34ebe52a9"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"620b500065ba752b6b09c2dba029be831b852e770dfc03dc56402a438547fa50"},"mac":"9a9b2bfc3867a93aac2178f18d0fc07d98ed9e09e1f8c6efb97af024d049fb33"},"id":"de78f8ed-187f-4bd8-8a39-813f17c6b7f4","version":3} \ No newline at end of file diff --git a/avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.crt b/avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.crt new file mode 100644 index 00000000..5139a918 --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEnTCCAoWgAwIBAgIBADANBgkqhkiG9w0BAQsFADAAMCAXDTk5MTIzMTAwMDAw +MFoYDzIxMjEwODI2MTIwNDQxWjAAMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAmeIXy78gz5v+fWJw+kC7grzTFaHGexN8rUqFI8HvPIkgOsUWObhgPQyV +QtpXJ0tTdGwMDyoege/d6Pn6pUotVOom2QiAK21XRffvt/uO/irUsSU/eWpksnWX +lAcKG1WKfnOfYASkOuO1tM7KLmXMmihxiazpOHAyonp83QJiJ3TC0G301wc2g+xA +hWRd4xIR9vzeWIFxUb2BoY3xni7iuqiOzk9NGtErNXSe0YNbo03hAEQJm54tVLFB +wZU/fGQefawThR9sMf8wl7OiDqB5PNMQVWKuNPCQ6Bk8ariyMNXhymUmYYS5IsjG +X9IuUQblg0REnR8YXnOO/IcPzxhfFpmphVEAdMhKlm/PkV3C502qnxXfqGKacwiK +lnguFnam2nhEZwyZddmTkr2/5RYb3phLx0kzzZbP5O1tH3DM2J4hjLmMBGwF2nmX +NOReYHg37+AvAEAe5Eym/myD6kdplybeCSxfA1YcXMIOS6JuTi+GlydvJgOkZjRx +6HoLUd6uzZZvTt5fyqJ1zQ7Dd1MdyCyQ1i0ntAHyRJNSc6GJRcTD+Ytvao5P5Y3J +XezRIZCILS3naV1BxnXUHvW/YHWkc1niMCt8Ygd6AWL5r7ePQE5ZBlDRlBg/eJAP +aX8rnoVMYXo8KbBKtvEekpknAsX4/Ub0VDtpmJe9iGGU+07qKVECAwEAAaMgMB4w +DgYDVR0PAQH/BAQDAgSwMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB +ABHIfUxoMrk8nmW6wSs04Xk8ie4nY4ouWmhhBy1jKQ6VVaFrSswJBEVXOxOFGlvP +W/XHgKAN3DX9O1ChjoVUna3mthV5VHmrFC8HC9hKjem2KAzvLmRLKDsXAjAV0e38 +Tl032fOcTAlPE3+lQ4Me93xKp2h7QSDD7q/x/XdrQH4ufP4memzCS1bRV5ZKiWTT +w/LI6BuuP+D+rrYbgFDTODoGFFI24aT0bKkND5r8O7nmpOJVupP1NUEFwNlIg14F +8zDkRInlNW0rIBwm8Wp9AVAubnChme1wRfHFuM4UE7nY6258Az2jeeBjTIqIB3+r +wg6g3EH7DbL+5v9YVsS89cvRfz1a9/MsncCYCSkMYDJU7PZCAPmDbSCLEQDinNUm +BykjYALniaVbvLRlMH4W7NAn6Z86Qt+r7orMuqSFOdYE8oYib4HzcHKgRSWygyNG +/zcQftbkIBKRprB21MIpRWIOFtTHKq5DbCRXMNN153zw4JnNpayPgPtSqcPUYLyb +tFw0jlExB98aL38MaFhBfe9BGCyiesQ61CCruY8AA2NSwM62lWNLFxrexAXimLAM +xdgcndQOxWJDipEe7o/xrpaiEvo57Zi3mLqmMVziutFrqtmzX+X8MkCYCANxrQkB +QjA/Z2u4qn+N1PoBMbTU4QbWNJ3p4cRxi5Wp3R1Ns0WW +-----END CERTIFICATE----- diff --git a/avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.key b/avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.key new file mode 100644 index 00000000..ed4fc884 --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCZ4hfLvyDPm/59 +YnD6QLuCvNMVocZ7E3ytSoUjwe88iSA6xRY5uGA9DJVC2lcnS1N0bAwPKh6B793o ++fqlSi1U6ibZCIArbVdF9++3+47+KtSxJT95amSydZeUBwobVYp+c59gBKQ647W0 +zsouZcyaKHGJrOk4cDKienzdAmIndMLQbfTXBzaD7ECFZF3jEhH2/N5YgXFRvYGh +jfGeLuK6qI7OT00a0Ss1dJ7Rg1ujTeEARAmbni1UsUHBlT98ZB59rBOFH2wx/zCX +s6IOoHk80xBVYq408JDoGTxquLIw1eHKZSZhhLkiyMZf0i5RBuWDRESdHxhec478 +hw/PGF8WmamFUQB0yEqWb8+RXcLnTaqfFd+oYppzCIqWeC4WdqbaeERnDJl12ZOS +vb/lFhvemEvHSTPNls/k7W0fcMzYniGMuYwEbAXaeZc05F5geDfv4C8AQB7kTKb+ +bIPqR2mXJt4JLF8DVhxcwg5Lom5OL4aXJ28mA6RmNHHoegtR3q7Nlm9O3l/KonXN +DsN3Ux3ILJDWLSe0AfJEk1JzoYlFxMP5i29qjk/ljcld7NEhkIgtLedpXUHGddQe +9b9gdaRzWeIwK3xiB3oBYvmvt49ATlkGUNGUGD94kA9pfyuehUxhejwpsEq28R6S +mScCxfj9RvRUO2mYl72IYZT7TuopUQIDAQABAoICACatRP3Y1A5aTuSE0ZCUpIx/ +mS5estxMUZHcoQoTvGWisY8T6jontUii2QEsj6s4q4kffwhwtapC96uO6QiSVEJ7 +d7glAtHncYAMiRvdBluruDmt+v4xV43jMG3sNwYXwe92cCBaCk0i3pDLSNXQkFRO +xfqskxG0YYWk86045CK2niB1AozpKzKNNspQJ6ji6dECTYBBSJIa53ILkycG8cWo +h/wiwmZzHEEbRq+yLF4nPN4sO535a7tVsglN0d8Z6u/7SAy1kSQf177JT3oNq2s2 +QWy0mMSFkmZDsYPBWEgd+do9AiCQk1fCztlWon2LFjSf+/7hyeYzdUTTTY3nuj44 +Hsu/GzAlQ2kNbvmUKDGECg5y4GoP6b8PQKUo25XsuPB3JFqOBl0QbXWgqW5f4+dN +fCjvasvs9QZgA8SiFm6T1dskiLFPNSFTlhqpRIpyHQ0zTzF7gAhhNUyc/DDjZGa1 +KIYcyzE9Hlf42V5Su+rDmQ4WcxL7nQSjTlkmhWRAhJYFtu0c88YzFG3aQxpQ06Xr +AFfjwn2Xo2+mKiHfzqkUXZ0LceFllIrOQpiaCrEa6OJXSWByOQcyYbNRaVhkqcxC +/FQjocI9jgVXU7hoBV3VDe4UCGAG3GlVqzY1KpgezCM0Tc8WOetYrT0IEr7qmCF3 +Op1+XBINxtEY1LRluYABAoIBAQDAZkyiRK5/4pzaoWU4L6Pqx6JEnh0go1SuNy/0 +GSsacaF2MKcnHkYgfZLwCBP3qD9/nxRJrQKYHiVwQnthSK97TpAXSZk35xUNBZoQ +0Mh6AZ2MUt5aosRs6CVRfqIJiohwKNdhJ78IFv0vnezROUgNYDWSLfJz4gZyRKU0 +COaESW60S+XjiHo88lCDKk4hb94OmGhMR+Z30W40syo9M3TC4IpPhEK1AkJGOwAG +2VIFWvHNkpOSpnyEMhIOEUyylmY8kJDLp1rGmPjDrZx/ZVAl3+Agvj45V6uStglR +frLyqhMkks+oMP2ImyhkJDnd/7EmjW0ivD1mwuetzlVOeSZRAoIBAQDMwF0k/W7+ +nzRg9d2FKL8nxfrAXVFQVCKQigm9os0khlS7oVZs/TCcsu/LPvsfaTCq9bTujzIR +83fLlj2FFj2dmHE4ZKTFXU1O3LqKdUqOEoZK9soTdteeIlkCBHHmQl3Dxk0wRxZr +3gFXPGmSAvT7rAMZJzGXt23Nkado2Foaeeh7vGtIKXLat9Dc5Y3XkfWTnZ8srgyK ++4x9zUn7w3ax9o9LmeQ4cw5JNntK/7gVB7nxnjH9b4B5BE8AWYDVDIt8bH3t+fzx +zLj3bqTBFpNeX/xB4QrJM+OV9Vo0YH+BqJ2m1a+CbX/tKq1jY+dorCX3bukx5G3P +/IKejKDdyRMBAoIBAQCZ9YKfWux0Q4eW4A3aTy0RI9zyob/XLTwHCPxjjXxTddhp +dndbszsUpsfdMemghU8Cte6LIlnwFjeJRHnsmDhsKKrfJ7Lm7BCJUhE+IYQ7NW24 +K1vpFVSvfXjXZMkPb37MmbH3+dFBDPxfjTVDG15P7cwQhJn+J0FXUR0NrY2hEA35 +Ur7MO5rtiE3rGABbe2QrM8RKPkLrYHiazUAgpzto8jtbdA/ZPsx/3Jih4mdjB6p1 +ATT75iO/uT1/alKAmh+HmXt0rB/UyfZOx29y9kSTejmkmfe69S/Df82nHAbgbj5R +RvXKtiE7nkVNFTwFLKW6nvNVF2LfB1pTV8gluoZxAoIBAF3zhsoKYHwFoYs8RkFr +yQr/FWhfaYhqktGwBXg209sGqKrQBVBNfZjpoo6of6KSKfrIdBVjL559EWMhtWj0 +I6HE6W7u/4xwm6CsOAyAdY1rgqOzSlZUGew95om+cGUv3saOn6yfcfJdFFBrMCYH +pVI2Q9Wu3lq3mHLTevXpEK9FcSAV/URHgKRL5FL4InXgS8dIdFmUI71CX9z57hDQ +SA6xDF+sZcRhRxThIwHvBRDhUrlD1bxtBY6CI54Nc8hO0VJulfo005RAVD91TbQs +H8MWWDICiWh5ntyYkKOoA9oC/RQoyTgX9rNk+dq5ZN3q4CEBi4+RObJOM8F81fPu +6AECggEBAIwRiB0/wmA1XIZR/SMFqtdIK6VJvaG5JzEgDLikuqRlTnV1GXshdSSm +FDtRSxMBLcJAH2fFDFsZRZGGfZsPh44r3lDAQJhwFM9QjHcQMX7VS5sVIM8R1X9w +8KUM1SWE8Qlkv44t9IBT8l+tMZRXEW3p6pZoUix6Hgaz9mM7AvqOgGvZlU3oRB4W +KBdEW1MizCksCdPdVC2BxZfzWOJnuoBNKJFYoKC5QgaK65lISLv/HISliHudzCK/ +5PSF/AwncFtaW09XrwE7IkpvRiwLgrlkYv2kzKb240vyKCo8DNbIwv6VSKyOgnXh +YW1yBDc9rBt9xddk+Wwg84zWlwe2SEA= +-----END PRIVATE KEY----- diff --git a/avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.crt b/avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.crt new file mode 100644 index 00000000..c61c2cef --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEnTCCAoWgAwIBAgIBADANBgkqhkiG9w0BAQsFADAAMCAXDTk5MTIzMTAwMDAw +MFoYDzIxMjEwODI2MTIwNDQ1WjAAMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAv6Tz7f/TD4Kzwh2dl1wmqe4SxU4LBIFCXhMfkCuemkZu/OdZf1o+o7Z7 +DGnuMeSqYjXf/9MpXZTDCPc5Jm/P8cohsg4HcJpthGnwHtuYKJH1zsnYj60X8Qkq +hhsRM1s0KvnBidECSyDr8515cjfWeKURm1PN7ayfnevyZZC6lOh/Np4njT2+2Pjw +tFTqxDbrC3spJF3xYTKGiVoVbeB6tetqM1jaWPyuBVAujt0PCDJiTma/dmt5L6Do +vVdJwZ1ET6LG+1kqAiaVSA6B0hOpb8PFkvOriAY8tIvr0vAP48pJBO3QttFDeJkE ++96HJiSGprLsbegYaH8W3KuJHpfooeJ/sAKD+EDGN8Q4hNMMvttFPjOmK+Zptlkr +fFLZmNAAfhXiiwIZHmxWDmV2TMlPbgHfiI/KKZ2BVfMIjD/OsEN/FQ6cCL3eJAyw +JkboQec43EFCJ2k7rcU6UoXt7RRmD8xEqeC89OGUwExvJUrkFQUvRaFge+kn2j4h +kIstMSOTa8J33J6AD85OyZ/1bUECh6R8dyIAXFxF+pf2umSPH5ebdXB2O5anj4yC +MT8wcfO0g0x6EjMhrqim9bSjcuxDUVwoTUqgCAIYuZNTpUKmNwv+uIUvyJl7okg7 +sQdTzXz1d7tAlfM0XVpFL1mQJ41wb6xz962NfQEUMUssgV0bWW8CAwEAAaMgMB4w +DgYDVR0PAQH/BAQDAgSwMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB +AG+p2UBepiZrxKXQjokmeUaVTs5OUAItrEKIQarxPa7F2vS8cdYqF+O7FWeBLWEa +MVaq1lOU6HU7ombFRgBB3YqQMWQzx3q0BZgE57QQQ+jW962/INi6je2HWyHYueFo +NBgMch8JfnF/7I5zi6Ntzb1jngOWo/qZYloraIapGEnImSO8SNYu9LKb/F34dX/s +ycpswOqxpRO5BFBkb2fKCQJWUkANYO8Kj/F+n9ydw/3WAVEX/+/R3iSQognyCZ/a +XvnV3Ygcq5IKysO6s3i/f1vVSznyXJWAjeaQIISqB0CMx4AkXQbbIJ5Up/ov2xXC +RzmZOcL3TGFkVdeAM6G+e6cPKzqGt/EzBy7n6Unbx644lc1dJmNvSAxEQ8oSMrNL +0+SVYJHQzZpfsiFSHN9XaHT6o8cMZM9NOdCWNyGDjBYM35pse47WJEOrp1ACnMsP +C3nT0uRNYKeRY/dQVJj759i45OztxahUTuG6lknqZ32m2nog+uWHkbPBEtMn9fRZ +QrCnYbTGUe1xHZENZYgDn803u8yc+ZIOLuTDTiPPb8a8pWosYRtQ2x3rDgJBPYqL +C5HQqcNAZ+HQYJrEctBR7SQOi17Yo8G6RveWhs8p4pP0dTxAxtyNomBi7EiTBj+I +MoStJDlwjzqU0J50872gHOyMZf2wdYz1JyFvWOhY0aUC +-----END CERTIFICATE----- diff --git a/avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.key b/avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.key new file mode 100644 index 00000000..ec4def9f --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC/pPPt/9MPgrPC +HZ2XXCap7hLFTgsEgUJeEx+QK56aRm7851l/Wj6jtnsMae4x5KpiNd//0yldlMMI +9zkmb8/xyiGyDgdwmm2EafAe25gokfXOydiPrRfxCSqGGxEzWzQq+cGJ0QJLIOvz +nXlyN9Z4pRGbU83trJ+d6/JlkLqU6H82nieNPb7Y+PC0VOrENusLeykkXfFhMoaJ +WhVt4Hq162ozWNpY/K4FUC6O3Q8IMmJOZr92a3kvoOi9V0nBnURPosb7WSoCJpVI +DoHSE6lvw8WS86uIBjy0i+vS8A/jykkE7dC20UN4mQT73ocmJIamsuxt6Bhofxbc +q4kel+ih4n+wAoP4QMY3xDiE0wy+20U+M6Yr5mm2WSt8UtmY0AB+FeKLAhkebFYO +ZXZMyU9uAd+Ij8opnYFV8wiMP86wQ38VDpwIvd4kDLAmRuhB5zjcQUInaTutxTpS +he3tFGYPzESp4Lz04ZTATG8lSuQVBS9FoWB76SfaPiGQiy0xI5NrwnfcnoAPzk7J +n/VtQQKHpHx3IgBcXEX6l/a6ZI8fl5t1cHY7lqePjIIxPzBx87SDTHoSMyGuqKb1 +tKNy7ENRXChNSqAIAhi5k1OlQqY3C/64hS/ImXuiSDuxB1PNfPV3u0CV8zRdWkUv +WZAnjXBvrHP3rY19ARQxSyyBXRtZbwIDAQABAoICADVAvfOdieWeeRrwpdDgb3H6 +0MZ42EDpNirz/2pNZKYT+lVWaB0mU4+kbm7VpLXCh8IAnHz3DWCOrN956yyOF2dl +tpVGprSYhOQtl+bLOG3VmUtb9Ux/Wc7FsEZRkScHKejjO+9y38NGo6ovUQb3Eya1 +H75cvDVWKYh1fwbP/7jPHkJsEiuCQ90A18oUaqQZapjnIViUrjVHF/Fk0d2VaiPR +BdK81vexRx382OMj9EBf18dXb86VMLiWavXqInmlnMzQO6Cudyjf1c2PEm3wvr1q +1iTuhvSmXwoAc4hK4u92T3IKL1wNrET/qE3g2Qc1USFTSLWFr/qhjhXFqLWSmrae +Ik2TvSVYTuZ/AV2tMJxg+HUUOV1dvXQqUaQ+50/0oYSs/X76rDozozwjequyKjAm +jKUyBRu4KEGM5vXuXtFsmfO+VteJN7cXbkMoE8vN0UDphfCormWpeftQCER/rdBM +SDDKugZH+MyqhfiOGydyg6w8yD2UT129ifLgxXlrlcTagH22kzqwOYJYCcE/PUO6 +xev3MDl+iaPw5EXgSJZgxOdCDBoIghXGb6pJvHA8vIT/hmCm6aPKLI5qpQzRzzlA +AEyt/rBDeYWgeqDD4JWchypMT/nAEARwxcESjGGkLycTXtCrIpaJUm0NSuP85B2H +i+I6GfEmRRjqb1jNdQVJAoIBAQDfhoWuT2cF1R0p4pPGTTuybz8e7OchPxaicfyb ++mEzR5AOmWUtO9HHmxMzRGPDTXiJa+x2OtHY6WGqmIfhqBOu1oMxpFpFqq52A41t +9+5PPJLyFTnDMVU1r44gOOK3zGBbMS0rV3kS2r1VaJ+5P0N4ajn6dHmPTSHSRszv +DKjTQeyV5HKGxUsyzttBRfcImnLOd3zY6r64CQEs+iwk9x1EOuc6i1iYq9faDLcZ +2sYjJoxcHHNm4lPlai553BuFE0Voav/yl/nRDYBoZ7hf2rshn6U/LFTNxhb53pLi +hw3VAUGc2sXEsEG2PssQWwv9tBDKpp3W25WHq6W2/1vs1t0bAoIBAQDbfLDYDc9N +L1bFIOSvfKzfsodfTmVMMNMVNRV9GiJJsUc61PpARkEFFobDKG7d3HowwvQapvLu +IjaW9InzccBkpaQXcC8CiWbz5e3ZFHvjQyPAPdY79lLFVLDxDOfzKkfLfJfz+5Mk +lpsh1eHFRE50ctrsLRqmCEs9D8wFKMgQOlLuerLL/ShWq/cQUFTDgwQQoTrSVq2W +BjMk8wrC6W882mtHUMm1UZcpLowT1kTbBEaWdo5DIIt5arzdz26V9MeaB/5oP6KJ +lKIicP1XDxuFpyoNyz/zjRufaot21Sb/l0Ymowv/LlkhFJk68KFDy8y+5Egj+vii +qdp9YrOldp49AoIBAQCockvTrGrK7mtn4SPT0Ogn88XxnD4hw9Ve3d+r++e8KTXa +iqbAi8/vSGITxHoZ+AvaI8ay4mi5ukartP7Rg0QyAd4kTuLolVOcA9lVAf5JIDDs +1ioNeuPn7UCB79yK/i2vC0Qq4WSQXN9JueqEQ7SozgSbRaNF5q6SNGDD1Rp0+b08 +yEe2YH/m7hQ1AHDFvv69sTYYke7f6YlFR9J0mPUpMeqHJ/PAOeAUpnhlt4SRgL9N +uvMMmYr33CjiFIl4o6kuo1P2kajlbfNSurcrJT7Ye+Kf1NyjpaAy9RRkObqmxqeX +eRaIvvsGSBPnY3WJ32S4Ew/nltr8vtHHMnEn7lP5AoIBAB0hTW8/VfYOhFuhY/iO +eXsu/Jx/kR4tlLZJ11JFN6vEJYn/Xj9Nk6+TL8uTADPd4TG7jh5DbhaA3/Hk/rIa +tMgm28JolzSk+87/AzQ+bbrZ3FLVYiuPI6kg9mLnrZkuUV0GajxtdTfz7R7ufgpq +aGc4HyodSl4i16mBATjmErdkpkEUc8pzDPmD1USLBNT1/IIgg+gKrPsuU3dh8lmw +FZ1j97z9UegIriE/FkRDudu/k4bAsVzjYt94dRyTPUAVvrAIw5EecPoFigc8K8N0 +M9jnOMOf6Brw7WuY/m2FGKnvb9YM6biydA3Z6ARUj3KqB8CGF7T+yCT+mgruIJ2j +QpkCggEAJIogtGpCDPrpYC2D00YdBTblbsV10v9BHTxphPaUMOxNnP4OmFGOso4D +gmGx+3ekoYM4cOyeguUpm4v7NBosiAsfJdcP28sRW3rZSqHRGy1cimaeyn0VtClo +bEABbagxN2VS3hUk6WkB/O2Q+GXQp2nRR6/H3SZtTF77J59GWuQbPQ+6VQzwVWLS +N76hKo8cuS2pFQBTHc3nrMNc2g2+SisKAT0ba+/DYQzGxwW9fkUh7Fq+vL4jUbxi +L4xURhDGpb7VNeo6A3MS5M71+XHusVBcwzp8WFiyBvn3lmrb8nL16TWShUPRZvPS +f7PDbmz21ZEbaDlbHZdpWKLtHPlI6g== +-----END PRIVATE KEY----- diff --git a/avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.crt b/avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.crt new file mode 100644 index 00000000..a4c9e668 --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEnTCCAoWgAwIBAgIBADANBgkqhkiG9w0BAQsFADAAMCAXDTk5MTIzMTAwMDAw +MFoYDzIxMjEwODI2MTIwNDQyWjAAMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEA0RQlufUGsuy/4OYFhtlj/Qaedss7zo0Tx0kS9TCvvdGxV3IYhr3A8BZz +BOmYOjq3OnbOZ5EL8WkIxmmXnDSivWd/lL8AyASHtXA6VwiHpaNJ6U7c2c6cfyf9 +fHgKw4wbJZx8ElsI7+dD4PXbIvl1PVRG3Jj5uY3NXm7P1io7hq1OGAG0Io+8Djqm +hZhzYDmBOR3ugZAS9G2wnpXA0cZre9GXM2GeLRF/BgHZ6nao49ncIoaa5zNgVgjK +yc/Wnxg1Y1niMFAL8IUtrajRWNjGW7VYKerPrI9HlhvCNiPR4od1NUzE+trkyl7h +mjvOjMUuDl3B3+JdpgSym9IyZlLS9BUXG8uMhIsmk1KhfmsgBPjuHVt864+ubWzZ +IsNn5S3SaQXhiu4LMY4EZ2n3Lfw+0qW5coLoEUJu86pH7xVrZVby4LrnS5LlkvQ6 +omSw7OAsk+vU0WmhJ2Mxe7ZGzzxmutPsleRBKSDCVJbHkMMdA03829nJCsrK0zSR +9pqpwR7HtZqg8si4l9eKN7DD3lJAV886+AoROtTf9aSSwRE46VsJ2ly+zCsFFpqX +jWAenqpHlM34l3bJ7NHMPYvCwCT1wGBMqSWuZa3iMFGzYjgFr39K7vw4aROmZeJz +4EUgrqwxH7zvskKR3xEIutyQwyMKPLAFP7oJ8hpEKk6WK9U0jJECAwEAAaMgMB4w +DgYDVR0PAQH/BAQDAgSwMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB +AFC9xcIlCcfaobe5/cLYsPOzPl4k9Wtk1qNODGdtLFei0UaDq4WieoqLxvCiRwcw +4euOIm4ST7+XOXxI1hlp4RP5YhdWiA9skwMA7GxvWe2i0QgihQ72/FK/be7gvfay +u06fUpHb6DMNy0ObBNCtIaRa6Wpy/knqcscLEB5W3fKkjlZbS4oQCD0XrGrMr6zZ +Kyn4Mc3XLK3FsTkjEwnoN8jUSNHJ9FztqoY6vO6VpFbmpWaGBUHIQ8ve+PEurFcB +wE+x79MUvC7BSjuL92FBHJRbIjxm3RZuWlbAeZ9E40XClD3W0qxDEMHf/nQeD33f +qfIDBbhlcfxlcSrkIq2ajSqlaHSq4ieUkcDM09GeL+A+NgZeRi2j9BhIGvY3kGm6 +7WVhF/E/4LcTkz4pEIk5ZQGTJwLbeC5iWi30Zg18SeCgiPx4Hiv/+Up3du/sy4G9 +rdaYaRa/edLtjO6+VSGWjVel+guw8SSdqYQ/NKcYJKFErPkcoACW/VXn15aChzrF +DyMojLVVDXnQzSOa143QVWyazpVnSc2IWBJksceBgUhcMVJqIz/aqRUvEnoD/3+M +S6yNtfqlWvaxH1jkxBnoFVIKgZKKSRkpSwS25RX3/I+N+fvALYoDshXzrk1Ak4/5 +/ORNTt32bCv1ESR0PFy4KZ8X557DnW3BFn05+qLsd2Hd +-----END CERTIFICATE----- diff --git a/avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.key b/avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.key new file mode 100644 index 00000000..a06a2b1b --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDRFCW59Qay7L/g +5gWG2WP9Bp52yzvOjRPHSRL1MK+90bFXchiGvcDwFnME6Zg6Orc6ds5nkQvxaQjG +aZecNKK9Z3+UvwDIBIe1cDpXCIelo0npTtzZzpx/J/18eArDjBslnHwSWwjv50Pg +9dsi+XU9VEbcmPm5jc1ebs/WKjuGrU4YAbQij7wOOqaFmHNgOYE5He6BkBL0bbCe +lcDRxmt70ZczYZ4tEX8GAdnqdqjj2dwihprnM2BWCMrJz9afGDVjWeIwUAvwhS2t +qNFY2MZbtVgp6s+sj0eWG8I2I9Hih3U1TMT62uTKXuGaO86MxS4OXcHf4l2mBLKb +0jJmUtL0FRcby4yEiyaTUqF+ayAE+O4dW3zrj65tbNkiw2flLdJpBeGK7gsxjgRn +afct/D7SpblygugRQm7zqkfvFWtlVvLguudLkuWS9DqiZLDs4CyT69TRaaEnYzF7 +tkbPPGa60+yV5EEpIMJUlseQwx0DTfzb2ckKysrTNJH2mqnBHse1mqDyyLiX14o3 +sMPeUkBXzzr4ChE61N/1pJLBETjpWwnaXL7MKwUWmpeNYB6eqkeUzfiXdsns0cw9 +i8LAJPXAYEypJa5lreIwUbNiOAWvf0ru/DhpE6Zl4nPgRSCurDEfvO+yQpHfEQi6 +3JDDIwo8sAU/ugnyGkQqTpYr1TSMkQIDAQABAoICAAyn2ozCDzC/cfHsGoOKHypO +v0LTlIAxO/RBMRuUYd7slX6EkoV9cPNXeIjglu1XZoXscRwFxKJNSFGyTJIRAZTT +cf6P/T0eQRSzgWSJSKS3GJ89vdKVVhXAVvC013n0viPtr/jwd78F0a8JUsVwMi+P +LWbm/57CvZ6suHnvPoEtdl+vorbA2pJF7JSDoOA/Oto5PJmtHeibxygiSnSoPRNW +oX9jtk5mE7lKXWV630hDhLa2TSQXkQpEJ/pFUWl57qlf5HgmMELT3Or5IArvvDoK +5RZMd1urpJqqfXwHeQnsRyT9y6Kfo1+4lJFgc2VD/kCyXizuJ9sHx8P02kYlV02B +AxsvnbgDZ/zOq5ZO+F2qrFQDRvouqP+VaOIcybSm45IYDwBFp2apzoFIUit7PIyH +pf9sh5TpSNVK1HdXyg/xAiXs2JRfEySv9f5zEhHIO+c0+/MBAxN7bFcPeROUxBNH +jBzqyuB2Skr9afWPENz1Va+VzAqJLALbU5jfivNE2GMhS3x0/IvdGiLLE0pR7+Yh +cnhGaUyBU27Wepvof4vqVqapJyLSl3kwFdMPZbsxlvNkpybGpA1pJF+6yyaNhKIQ +eV+ktpM9CqW/XyDv4+nqKBksaA4mXonGsw4xiD2NqOiNOtvzgICkCo4QLjV/hTcc +XxMOnf+smxk4jBRdcdfNAoIBAQDoamPX0rVC0AsV9rC88IbTK9ePZw2Pu8lds3PK +S1oGuFPZg+SPUbO6l5SG3ukJaXdNazKzkCK7bo/mbwKRVqyOwrONkvRbQKQDlOrO +FuffbyYUkEF+8ln1ls+R8gmZfzPIXp7z3pUa97TCaEvxZ1GvL2nl8IUCa8OaXbnc +bfaBrC6EZbOCmpbnxR8q1StGeO/xDKdVTrHEMYfmGzgjPGKZSfjjLwQKG82LYLHo +pZRjPCjwZKi+RrqPikc7z+hQ1WuvfNedh+VtnRhDDmQpZRqbPHG8A8iglQ4yNlvH +CzXXbC+rzhqflx2VxQ2y/cewcynVsZQ8/3MOkm6r9HIQNmVrAoIBAQDmS4UeUsZ/ +g15QIWpUP5WzBUcMfUhv5l0tH+gKe4HB1hERMQlijZbJWOCkyrbSb6s5KsB0GCUx +5liO4cDwqa4QpvwiAPNodkqyyAWakbRRXHkTqPmwFQ3tpd6uzw6yepgKoDKCLVKT +dLaxZhxrl57Dm+OxJLa/wNNfMTD9OoNRj/IaIM4NtzaV8NsCmoYNYMt7y3g1JG+6 +Z9HW5VgSWnV4NekaMiWWyvgqDxiQhlw2g9cDz3fdkK4mAaGzE/2Jc0gqGrumVmMb +beRdk1rrA9hSmYkHFGOh8DpgGhie96cK8lrb7hVzlV2+bhFE6xb0tLfCgJ8NuBHR +XySF9cCkANjzAoIBAEnetsk8IXyQ31UlyVrkIBYyUxszJ+5NQ11S0jSz6nNEVvJF +p3meAEAIOZ78bpkdOlwS/FRyNXfZVkdpdSl4oPPnEmanFJ+EVh0FUfQa96veif3z +M74DjXAvdN3wTfjJo03GNUNRtZaSsJMxzyF1M1M5ALGi1sPEOLFxpMdY9AaXEVnn +cTzLobccwiE9C7okai71VKIalwnCrG0IrbkVUkliZ4bdxKzp1VS9cQ5+wkEgN+rg +M3n2xQkmbCmolTBMBSQLzAQ300YZrkJx6of1qJU6ivO1SCkx9wcim90RxRUpP6Wd +pnye7TpTHV2Mp9F/aQaQoIsarhktlZt14GMixu0CggEBAJjp9E4aMGti0zDpvyyJ +u+DO1Epatcp3b88SrISpVZNbjKGZqd2inki7lw5U++fm9g5TcM6laa0kghD9nZsv +T8EG38vETdtDEH59ihsEPHL5r/t567EbidAOnzn7iorAFEyZ6m+9oGMA0H4iiyR1 +3JmN3TY2ZpIUe0Vt6FV8kM2LgvoOZkN9L+WHtCSabgBcEUD17a97riRb412Jk108 +a1uSkGEwgv0JcJacx3bPgFNCX3bMP48DDl92HeRGlca6oWQzs/3SxqPj6AqqWWhL +eUv/Xpkw1KcmV9yP92E2l9gQ7Hu0LtI8j65ub1EPuIJrrMidlv8JBeu7/QhmEa/U +UDkCggEAPmWKlEuS/53KMi+lkzaeqXz3lBLYTH1FJ7takofecAkdv1fjEDThiEg9 +REWOQPyrggFqfn6m8nrYCUzFMZeEfohEGP3VS2mpkDSm0TRG8RRmXAezX/65XEgC +fB1sN7yDWk2155DA4eY0dDLR/LMfcKo+/xmQgByBWgaEmjvmcO9FzCTDjrshCcqq +mjppUBo+OhQl0ciLlFyU4jW/O4NTGkwcTirSKFuXxMINYJ7kNGjCUoOYxzx7q8AC +bCV8qfj4smsJtm439bHFpDnnEYQCgZ/q+ekGIEVdLBAv4uif7bASicbZe49+4crC +YSrn5g3JCxx5Fixn3NxZLCjoK0fspg== +-----END PRIVATE KEY----- diff --git a/avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.crt b/avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.crt new file mode 100644 index 00000000..facb3fd9 --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEnTCCAoWgAwIBAgIBADANBgkqhkiG9w0BAQsFADAAMCAXDTk5MTIzMTAwMDAw +MFoYDzIxMjEwODI2MTIwNDQ0WjAAMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAoK6U+KyVEV+2303Ht5tp5gQzEZCg5ndr578d+e/NhIWGSMyYPQKuVZD6 ++fuCcOFIJnOm4zhkRbuijnbd/J2/kYWR1H9xnpd4rEFelh+r5mlNSUBBbepMJi7/ +9z6syiRLvXIYC820fXJxilX//ZMAcOWTFSTQ6ffk1ZB3hgm6lbSo1lThyiGstnro +ds39PUqSB7IpV2h+xGr0xPdi2hZrrp95NZkOx6wJE4o8GuKP8qpKq+B1vwRFuZ0S +2DEVV5W1pienIDTAmGtP1xxyUNDLD+hOS1CTNQcTgQurtJS3ADjZfMh8CGb3168w +HeooGV+xbSmbreYbV3uNTWctT1jE0zoY0QFlfmXdSGF4Y0fMZ6Ymg9nsZFsmYFbl +2bo8R+5zZ2JNK5oRPchxVp8w5mfv4kr7eMRhM+0LSXLHhcGDVFWEAfDUCfgwNECG +cIXaUSskMWPLvU1oHeXOR/+i9i6JQXiDixCRtwTGBxGG+KFe/3QWwnbXuTz05tah +DiKPEcW/xpVJFeNH/LIjjyNiXsdKiDHN7LUzDQQMiX+mhiDEMZEhy7FThZfUB0vT +JwmG9WIcd/UXsQKJEzhk8IM454fz3sop04Cc9/R2oKfg7Uiv+W36Y9WZOGZRQqAv +iy+GV3bFlpzscJFZ9LtYRnUrMh+HQ8vvG/S4F2tQNGkIr2zCXCcCAwEAAaMgMB4w +DgYDVR0PAQH/BAQDAgSwMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB +AC9Jb94geNN0tlr82jGt5zh+m1eqKNS6b3ykEu6XAw5rfa2SAdlxNPl5t08Jtw83 +3CRxj5C/ov94a+KKt5bIWJGvwLAEVbMyAV7ldTb/uP3HO/PIUqN+xdhW+iu+USLA +bes4DNxTTAe8VrS/s6PNaHKowlRZFNdzDA0kX7RLZZhyNPgZ0pw8lBY3bRJXgx9+ +3EU2h6+B0PVYs2UKlZk63he4PAOvO4rY6Z7aLbgfZmAc5z2ZcdrKWvGWXe64DBXP +GDpO2H3xDOSpNlUeqVKIGkEisD2Nx2fpIL4fw/FLe0HSLfzCWydFzpVhb2ZPQX2w ++ciIGrKd1nOLDmWjMij9dGC2n2DAQJvoz3OX5P5crTNupBbEIRbhSNQq+aus1T2D +nuOB3ebJ2VjNvnosMybeC1PpCZqhmFbcNfixFGjdPfDQwGSwZb6v68DUGKeZY3fn +LKk98ivdL0UEVi7BcwXVesw1qYvCUiecUIkVzaBsd60eodFHrJtXeONMO5grpEjR +Dlr21nW1OJhghmvlr08cFs2n4SMyavgtOq4Cak4D5VoiaCbu0+8EvBJAKVGq/RKy +vBDRZ+nLYWbhqYe2ZcpKuK2VrWZJ4tll5YkkKGtwLdp6PaqCJQbm3BxheYXMqRi/ +GN/0HEx27gMtelcpKxftKvvIkZ+xWRn1kN+8cpC9wkcx +-----END CERTIFICATE----- diff --git a/avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.key b/avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.key new file mode 100644 index 00000000..fe7b875a --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQCgrpT4rJURX7bf +Tce3m2nmBDMRkKDmd2vnvx35782EhYZIzJg9Aq5VkPr5+4Jw4Ugmc6bjOGRFu6KO +dt38nb+RhZHUf3Gel3isQV6WH6vmaU1JQEFt6kwmLv/3PqzKJEu9chgLzbR9cnGK +Vf/9kwBw5ZMVJNDp9+TVkHeGCbqVtKjWVOHKIay2euh2zf09SpIHsilXaH7EavTE +92LaFmuun3k1mQ7HrAkTijwa4o/yqkqr4HW/BEW5nRLYMRVXlbWmJ6cgNMCYa0/X +HHJQ0MsP6E5LUJM1BxOBC6u0lLcAONl8yHwIZvfXrzAd6igZX7FtKZut5htXe41N +Zy1PWMTTOhjRAWV+Zd1IYXhjR8xnpiaD2exkWyZgVuXZujxH7nNnYk0rmhE9yHFW +nzDmZ+/iSvt4xGEz7QtJcseFwYNUVYQB8NQJ+DA0QIZwhdpRKyQxY8u9TWgd5c5H +/6L2LolBeIOLEJG3BMYHEYb4oV7/dBbCdte5PPTm1qEOIo8Rxb/GlUkV40f8siOP +I2Jex0qIMc3stTMNBAyJf6aGIMQxkSHLsVOFl9QHS9MnCYb1Yhx39RexAokTOGTw +gzjnh/PeyinTgJz39Hagp+DtSK/5bfpj1Zk4ZlFCoC+LL4ZXdsWWnOxwkVn0u1hG +dSsyH4dDy+8b9LgXa1A0aQivbMJcJwIDAQABAoICAApnIyfybHhCfSF59Kszeai1 +ruS9O12Qb8ECQ5uR5flld6sw685PZSAPEeCrYueR/kzXAgAF/5/wJPcaRQb845E1 +V5qtYZrF7b8hQFFx3Xw+Zw2OBm5qU+HI+zNzsFYqAWlTgLEK8VuJ1EYDiV5ObSHi +CyK8TnZHYV5eSw63hIf6uU+fETs38OmjjXdboMCeGlQ7Oaf6EPMAhbZW2eSKvRAC +LSPGNAnDXpcQRRHshytL0SfNkXH7gdQhkj3C98HC5I7MVGqmX62+jN29br1Ct1Lh +VGhV4CH3wlCBg+1eD3cJNzN3+2fD2tnw8qmNJml2I5OQtQ6OLkj8q8pOGsXfVmbI +QKkKVJuSZrU8fLd7LJplfNKXLoU2GFzJ992eC70Hn72KZSnTmVqeJ2LboIjB3PZx +2jxWNO7oIOYN7i4XrXa84BaxRSqOaqqU4IEUl0kEiY5rIdasV2XflcQ9CdSBZBwg +BtNE+rugyVt7zG4+UrEyh+apsTidbREM6NW+4bDeSnOjBis9+n2nH6Lt2Cy+m9w9 +B7r3wPAIS/k6Q3poR5oGum3rmYrd6dvZlvFe6LVT3XODrXhg/wcCOb6HpkGTBGcV +QgcwyH01iEBzB07gDh2KB46JC45IUiN0LONluWtspPJJ6Lb7huhpQRkIdVBnrrum +oNWUn7rwTBBHDO4BE4+hAoIBAQDGssWmHPAVacLRj8n4y5AgUFYC0W5Kg4TNCn3o +cF0slZ2SbIn1d08lJLhKxjbnVEfyRwFr8qzO1k+xD/TrzqrDvYG+CxrOhwKojCxp +81Fs1vbnrEaLxRcIcq5t4TpqXELFY/W7BrHEWV13Yra53YjgKrejbONpEkn6OuBE +XE5wdjLjkqVm+58oUAZKfdg+iiAvjxjDLfREnRH/P+05uNsidRPOuWrbGeBB6hRz +cgswTy1kFvKY2NH3P5/NXN/DCb3OcKTjd6qBSe63PTL+dYd02XDpWbbH5pn5vdGr +qTIoX3Iriikf2/KYTd9Vk1JZUveuZhQ0OOAbwi9ijOGNwUR3AoIBAQDPBTCF9hWV +hH9lb5HF+ml3hiuq/nWiu2u36b5KgTfX15BdgDQHepr7v+rVVAlv+OwNARRuIs0K +ynlQg9uJpoTdkPMdQQK8gIc8cdoYYYCCHQYSpHQHzT+FHCNyIyQhxy3fiF3l++4s +JyQDAipy8Wm2jFb+JCoyY3V2vfym58g4q2JAWwQtCN+FxDTVFdf/wWF/zcLH+OwG +kdNv8nkPd80Hxs3YYKM/P2K+M/NIEG+PVYEKdSA/Xo+8D/i3++8TaU7rH+er8nDd +dNtAmP5yOxKyK1pM1p/5fyA27NVK2880bsY1eKt2sAeOpaBoZXFNOZYvPpCQNeAg +Ia8phopbMwHRAoIBABppErpkqEjAWQPPz/HA5PRWmUggnD9NQe6bEWc+4DOX42zd +/mxhImT2f4R42j1fR3zdkUPO6sdHVypay5w4SRxhaQhgo3yGF4VdtF2U2OLVo9Xh +YTMczFX0IFk8ka4nezyPb88fMau+kRzYQx9rYdEh/Gy9gXCq3KhlAQA1ILv0CgmT +qWaJ1XrfQCbQmVietW5837a5ufkQw56g2aBNgNsQosRcguaChvtb6MH7i58tNwRR +xIcqcqYhj9UVHiznc5IoCNKvSjuLHy699LQ73PhcGpT5tsDVVuwKHf9qKt+Q6DT/ +VT4F1zYFS8UtIsn6rC9DJDZYxPkLk47O2QAlizsCggEASeKSRN1lKq3VQgClyEDt +L0llgGu81E2LzmHQFoszwQcgcR52c0zgz4TZbTXHAH2Y0bNW9U/aWkWVMpU8303d +Ea7P9drj/R7qJTqFGjqCQO3W4wRqnPY1+23lgLDmDmAmESd8S760bHGzupclS0y5 +6/r+eUmy/bTdkXmRGIiUvxT7xfg171TBYd569KoNIyyDfbeUEwbWRin5inTBnDGn +rWTYxqcG3GXk+flUlJ25EzDzEyZmUfeP2RDiNdfmpcjp76S9bHLksUUpk06HUYGf +1JMAcXgQNF2Yw8GXBlaZBx+aAi+FEqEUsWaPb3L1ExjAbzAnLDDuQsmC2JE6A/6a +YQKCAQBN1m1gd4UbPQ7iE/Lre4nYYui9eMrwfFDYyW2lrY8Jc7TtNaS25P5Wx9jI +Eld6YqFG76cdj5Pew4gZpFbAHIyXi4AWWw1ex1L2HcLcObBr8sVgpJbzx9hfRINe +/QGA3TDqwzkMTtUCd3dBiqf2C2TVkfYBigmJz7OsK7VvdRBq8MTj51UnMW6lgFdw +PyJ4abLnlaL2Y18qsaMstg3fA6fNhUdXPHJusbwOzgXPkUj5Hfz1xye1XMuy3s39 +xjGAdW2TKD+IQ3rQFcfhobJaoglZr8rO2+cirm79MwiLUsxzJyfInUcj1QK2vZBG +0OglncKX+5vYOqDqgt8Z85kNw86t +-----END PRIVATE KEY----- diff --git a/avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.crt b/avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.crt new file mode 100644 index 00000000..46838ca5 --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEnTCCAoWgAwIBAgIBADANBgkqhkiG9w0BAQsFADAAMCAXDTk5MTIzMTAwMDAw +MFoYDzIxMjEwODI2MTIwNDQ2WjAAMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAsGYbFApJN75/27xHTh6uLpyvZW9X5D2H3Q6ZH/a6byRlDXXZtwT5Zyxv +7PKM0qSBRyx4kUZ8Xdn5/C0BE+2RMfam2zQ3yNxOYAIgHt81kzBty/w8+j73RHcQ +kRwXFLK6ftNUW2T90WbuOeliTCNxssApyy9RMZpU/uIz6tkjP1mHvKMkccsQBXZ2 +FoRuANTpBm3N1MGIQjS2jfldrkWvhGxl5vsPUUGVL2SeUjg1F2EkfrJ0rVJc9r65 +UbRmfVlEtE3YATW5aPx8N+TVL/BbZm2ylRnJaibYu5OV/oUdEHX5foL+Be4LUkCb +lJKOEfHlIHiwx71DOJ6kerjrjhpOxLVnsQ0dD7fp8lItiyQq9qN43XpuMVGnpxR7 +dxhq55oYtZ0P7WC4ImAXjQCrWdb7T7QgbJvE299MiQp20RsY8/blskakfCQQWOtm +kmYw3GqcITPe7l6kVi/lQ/Yss5ALOHg2lOhHaBOLIE4tY1QNtqNFacY0gKsLlImd +wSnPSABvXbr/eruRyTDq3JqXu7vultwbluGGYKp8huzlNQ0GKtlPYYbdUcHtO6ec +iAyMfUAbHcMpck8dZelAkwq1Vnn/oXDfbpA74z/tnh7Tfd0Dq2sw5DXoJWAsckcl +ye3m/OKJtPCuNIJi8+kor/0pu8ooK+EeskzAk9X1xT0HwtEx9FECAwEAAaMgMB4w +DgYDVR0PAQH/BAQDAgSwMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB +AFtROZguq0gqoqah7yX5o5/lQYQ23EaxVvAr8VFycdGjvbRxzV9tKGS4tLqvZCpr +qCb7TjozmuyHAtwvc9AFVcAiXlYJBKtIvcNDREbxfmxMW+E1EKpJdtUMNpr0EdN+ +mydk4Mz7ffEXKsYNbQUVAjleR4tsJANGNRc1/gOLj5abfe75+nfaUfBVzc+UYABV +zG+BmLnZEzPwBYbkxirrOgfn7hZCrhccV/hVFvg9TznJrp/3+pfjCI0YzfcHYvFj +NDJQ6CK7w+O2bDki+9S+MlfHOverIiKK6BaqHXGqwuoXms8M14jVjjdm+G9GoSr2 +VPS8voAAUaW7ks6vatttaWaSQ0/zmaeK6W1coIzme5CeHPwYjwRKaY8wzHJVVglC +XC1SuJwdNhFAVGi4MmG3zpINSzUxTriPXeWrW8lpQ5TVPILTiNKg01NXr21QyLWX +exy579Zc8TmghvSkXxV7wHH6ZNF9/gTdV9sNOe0ixdtzoOgrF1w5TOtSYjQGIoZk +WtCx33BqwiaiFf1/ArCIHn7hLjdQITA09/F6WxSTry+EvJfDwSN+R1ltMaY4CCcL +qmoyIcDVgrPODdT6Y2AUbd6q8QpF8CyuhnG8NT5D2uVva8gJuG0sek+KSejn/oXZ +hZ3QPYNZqNN8bfksfkMVEhv1dUb4DZYo/ATOIjmW177j +-----END CERTIFICATE----- diff --git a/avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.key b/avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.key new file mode 100644 index 00000000..2b645a1c --- /dev/null +++ b/avalanchego/scripts/keys/NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCwZhsUCkk3vn/b +vEdOHq4unK9lb1fkPYfdDpkf9rpvJGUNddm3BPlnLG/s8ozSpIFHLHiRRnxd2fn8 +LQET7ZEx9qbbNDfI3E5gAiAe3zWTMG3L/Dz6PvdEdxCRHBcUsrp+01RbZP3RZu45 +6WJMI3GywCnLL1ExmlT+4jPq2SM/WYe8oyRxyxAFdnYWhG4A1OkGbc3UwYhCNLaN ++V2uRa+EbGXm+w9RQZUvZJ5SODUXYSR+snStUlz2vrlRtGZ9WUS0TdgBNblo/Hw3 +5NUv8FtmbbKVGclqJti7k5X+hR0Qdfl+gv4F7gtSQJuUko4R8eUgeLDHvUM4nqR6 +uOuOGk7EtWexDR0Pt+nyUi2LJCr2o3jdem4xUaenFHt3GGrnmhi1nQ/tYLgiYBeN +AKtZ1vtPtCBsm8Tb30yJCnbRGxjz9uWyRqR8JBBY62aSZjDcapwhM97uXqRWL+VD +9iyzkAs4eDaU6EdoE4sgTi1jVA22o0VpxjSAqwuUiZ3BKc9IAG9duv96u5HJMOrc +mpe7u+6W3BuW4YZgqnyG7OU1DQYq2U9hht1Rwe07p5yIDIx9QBsdwylyTx1l6UCT +CrVWef+hcN9ukDvjP+2eHtN93QOrazDkNeglYCxyRyXJ7eb84om08K40gmLz6Siv +/Sm7yigr4R6yTMCT1fXFPQfC0TH0UQIDAQABAoICAQCC3t4MGi2EC/N0JNWKIbz8 +nAaM7PR6QyQ/Xws/6Z8bwhauy9aAsFIQt6k6VCEiTiYTlezFa1PtlUCgVQtNq51j +Qik8zBrvDHOhSzJldRicYPu0sSjUSE7PHr7Nyddd5dPzL/qP6/dbmk/36yygbFN4 +WJUpR6unb2bLyyrVe89oupW34BmzywHR67dksU+jklJoEKL6y7K5273syyszlkPH +rVxU58LPeCakiLPlMApDlDCtRtzFkiuibJG1CnBXYxlT48Tz8NXkDB5koYOBE4Pc +FFq/v+fst3FoiPBpanzLr5swvyKTZ65Ckehba/e3XI2vb5tINSRgJ2LEIdmxDdeN +YIwIzGI0/MEbA1ayShyxDpy90dGRRyj88Ovioh4yg5M4GIwxH3bPDvdakspz1Us/ +c1wtzkHdt5K451BnVoJqoJ8jDtG8+B4Zl/VPhg3UeNsLaNgwli7Qs2e9a2OduUhg +kkpvu1x1KpVbhECfCbPTOuXlUmMWTHMEyBL8tN81R7qbZ3zd1/h7krfX7RfyrTOH +WXNESQbRNd//U+VFeKMuGS79QBiwDNzmotkAo7hhqE2DyYbFWNG+0FBozjzu4tqC +hJKgXqwelCugo20BeMW5J8rI7YXUtNIeZmlBNzTeRO3yq+V69yGPfw7eqb3PC5El +u/SGu4q6Mb6SSrQurNYEAQKCAQEAwXyjSgfGfXKWLiLF9/+M5bnoGOVE+EU/gEcz +YqEaCK1Ga7PMdzR8LOl9kta2MccQtBmexU1EAH1BO6HouvcwS89okLz9YD2V4Yp6 +KM2Jexr0hvsLORdzjDBbss+KmR2A6XvyVqkrhzViJgrq4BkQWIV6dx19dw1kxrfA +1YcW8QZyUZqitOnmqH2nstQ4PB9Xs6wGHq6/Mgibj+Hg5qJwT4VuqC9UJnN4n7C7 +IPseb4aMX3SifudtSJJ4HXJoP6nMgNULIxOHQhdqRmEXo2qZGXkGnb5ro655xApI +IZCgfVX0ZIyX5LhG6AanrdvtvZyQecDSVpQa8SFW+tpFL2XooQKCAQEA6WQczrlp +yxPBgJVGqzjSh/AG69+c2k3sPTBMBLfoqdnl4/4uA3i6BNr4AlKA6wOCIzXAi9zB +018e5dq3isfR3jvJpyMApTTSG2CT+R+/stJO86fDRQ9UNOfLotX9BGrRFEn44CFg +g499bfsXXNkLg97G0ZwW+ILnI6QGgCwguRAMlNRiJXncF7baAOvK/kwh037w4859 +QOOYs6RUFlaNLsU//MZ7sBweijE35I9UrlyL9f/l6xwZs+beHzfYzM2dw6DvEjy6 +jSQsqztlMbcNLChC5LaxOXXVwng02fvrcFTZx1IhZg3LG2dlfQi+XL4PnAGADVUe +yMtafJCy+7X9sQKCAQB8mCAmWd9thM5Eo0vMKRm4DQDylyDUpvL/Tdo/FvLGx53j +WQK/eGLGIQ0l83GbmE6fSyg8cUbq8b2CAv709px5FPSGz8xxnvbXJ+8GO2TeizsP +eLV/bCSSD8nc2VyswJz0vUYOnB1VkgDD8p/Q8xLdjgaQkcLrWQqeFFHurnENgFM5 +TVSdH0nTEiddNt+Hh79YCdxfAbRJKdXSz2ed3NhpvMjHMMMFh7ITUyENWef9dS0L +gvXotmpfI0KJ49RRuqvgAlCLo9Wjr6RV8cvhBZHVH2QtTCPQYVgz71MHmjRewW0B +Gq/tLuUqIK71e/wZ76nxXUOVUQYQZKG4I7oNjZhBAoIBAADY+jnN9pJkOfmrkRwi +ErGnr36/yyQH0GnQeRYzTMcBIZKYY7Zjg9lsqiXoAsvXsWtLUK2xWYrY95XCG5SH +cT2rXeobeG8hJ/oteZwX/2w400yTOWRQz+95QAYRXN/dO3Ela5/0vxL7PGtf1A/k +QeRytKoiNPsmDozUlal1f5Dse/82XjzgmmoDofsK4CvYPu8zsNBC+bV4nh9Y421J +Vy3qTXcviVd0YrABycAsvRP9Bg0Yps8ZxQYgOoJGg6UKwh3pwbSBP/QvQ3JDvvRV +Q9j7NHL8LtyUjvgr05VPEuXPiRLuO2zRdAFfeMDhfT98+TWJ09s4ieoUwvlc1SmQ +2aECggEAWOpRvOjx4P205fnFyIOjL71oshktpFC8c4+jFLb05x1lSnmL7kG1LdXl +ZIH9XoSumPdfYJ0CxJKzNZYK7LXepAsaG3otwR4Y0ac0+rp52ZoLTIJF97v9dSE5 +KNlFXho2cRmG/VRUruRoP5MnxXMGKbmpnd5+1SzNOxSyhicDVUIq0Wz41xnY1aCS +WkNKnZg7Z+Y1jw9Ku1mAo0ZDqn+7KNvaHYbOMb27jbce/4F+z+uI/SLTUt6AE1Xd +Z+Y33O/D0TAkTKWBvFEJgvAJaqQUU4cLymOI9pV8uCo9kkOSeKsEhTe/ij5dRzAs +qZHLGzp4gZ/hyJL9pXdhNJGOQAVL6A== +-----END PRIVATE KEY----- diff --git a/avalanchego/snow/validators/custom.go b/avalanchego/snow/validators/custom.go new file mode 100644 index 00000000..51ca96b9 --- /dev/null +++ b/avalanchego/snow/validators/custom.go @@ -0,0 +1,35 @@ +package validators + +import ( + "fmt" + "os" + "strings" + + "github.com/ava-labs/avalanchego/ids" +) + +const ( + customValidatorWeight = 200_000 + customValidatorEnv = "CUSTOM_VALIDATORS" +) + +func loadCustomValidators() Set { + set := NewSet() + weight := uint64(customValidatorWeight) + customValidatorList := os.Getenv(customValidatorEnv) + nodeIDs := strings.Split(customValidatorList, ",") + for _, nodeID := range nodeIDs { + if nodeID == "" { + continue + } + shortID, err := ids.ShortFromPrefixedString(nodeID, ids.NodeIDPrefix) + if err != nil { + panic(fmt.Sprintf("invalid custom validator node ID: %s", nodeID)) + } + err = set.AddWeight(ids.NodeID(shortID), weight) + if err != nil { + panic(fmt.Sprintf("could not add weight for validator (node: %x, weight: %d): %s", shortID, weight, err)) + } + } + return set +} diff --git a/avalanchego/snow/validators/manager.go b/avalanchego/snow/validators/manager.go index 0362ad2b..15b5e044 100644 --- a/avalanchego/snow/validators/manager.go +++ b/avalanchego/snow/validators/manager.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" ) var _ Manager = &manager{} @@ -43,9 +44,18 @@ type Manager interface { } // NewManager returns a new, empty manager -func NewManager() Manager { +// SGB-MERGE +func NewManager(networkID uint32) Manager { + subnetToVdrs := make(map[ids.ID]Set) + switch networkID { + case constants.LocalID: + subnetToVdrs[constants.PrimaryNetworkID] = loadCustomValidators() + // SGB-MERGE + // TODO: Add sgb and coston network validators + } + return &manager{ - subnetToVdrs: make(map[ids.ID]Set), + subnetToVdrs: subnetToVdrs, } } diff --git a/avalanchego/utils/constants/network_ids.go b/avalanchego/utils/constants/network_ids.go index 9660a026..1c0e7548 100644 --- a/avalanchego/utils/constants/network_ids.go +++ b/avalanchego/utils/constants/network_ids.go @@ -26,6 +26,8 @@ const ( CostwoID uint32 = 114 StagingID uint32 = 161 LocalFlareID uint32 = 162 + SongbirdID uint32 = 5 + CostonID uint32 = 7 MainnetName = "mainnet" CascadeName = "cascade" @@ -39,6 +41,8 @@ const ( CostwoName = "costwo" StagingName = "staging" LocalFlareName = "localflare" + SongbirdName = "songbird" + CostonName = "coston" MainnetHRP = "avax" CascadeHRP = "cascade" @@ -52,6 +56,8 @@ const ( CostwoHRP = "costwo" StagingHRP = "staging" LocalFlareHRP = "localflare" + SongbirdHRP = "songbird" + CostonHRP = "coston" ) // Variables to be exported @@ -60,17 +66,20 @@ var ( PlatformChainID = ids.Empty NetworkIDToNetworkName = map[uint32]string{ - MainnetID: MainnetName, - CascadeID: CascadeName, - DenaliID: DenaliName, - EverestID: EverestName, - FujiID: FujiName, + MainnetID: MainnetName, + CascadeID: CascadeName, + DenaliID: DenaliName, + EverestID: EverestName, + // SGB-MERGE + // FujiID: FujiName, UnitTestID: UnitTestName, LocalID: LocalName, FlareID: FlareName, CostwoID: CostwoName, StagingID: StagingName, LocalFlareID: LocalFlareName, + SongbirdID: SongbirdName, + CostonID: CostonName, } NetworkNameToNetworkID = map[string]uint32{ MainnetName: MainnetID, @@ -85,6 +94,8 @@ var ( CostwoName: CostwoID, StagingName: StagingID, LocalFlareName: LocalFlareID, + SongbirdName: SongbirdID, + CostonName: CostonID, } NetworkIDToHRP = map[uint32]string{ @@ -92,13 +103,14 @@ var ( CascadeID: CascadeHRP, DenaliID: DenaliHRP, EverestID: EverestHRP, - FujiID: FujiHRP, UnitTestID: UnitTestHRP, LocalID: LocalHRP, FlareID: FlareHRP, CostwoID: CostwoHRP, StagingID: StagingHRP, LocalFlareID: LocalFlareHRP, + SongbirdID: SongbirdHRP, + CostonID: CostonHRP, } NetworkHRPToNetworkID = map[string]uint32{ MainnetHRP: MainnetID, @@ -112,6 +124,8 @@ var ( CostwoHRP: CostwoID, StagingHRP: StagingID, LocalFlareHRP: LocalFlareID, + SongbirdHRP: SongbirdID, + CostonHRP: CostonID, } ValidNetworkPrefix = "network-" diff --git a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go index 16b2a5f6..37216686 100644 --- a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go +++ b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go @@ -113,6 +113,17 @@ func getPhaseTwoInflationSettings(networkID uint32, config *config.Config) (uint MaxFutureStartTime, 15, time.Date(2023, time.September, 7, 0, 0, 0, 0, time.UTC) + case constants.LocalFlareID: + return 10 * units.KiloAvax, + 50 * units.MegaAvax, + 10 * units.KiloAvax, + 0, + 2 * 7 * 24 * time.Hour, + 2 * 7 * 24 * time.Hour, + 365 * 24 * time.Hour, + MaxFutureStartTime, + MaxValidatorWeightFactor, + time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC) default: return getPhaseOneInflationSettings(networkID, config) } diff --git a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go index dfec5efd..be939d85 100644 --- a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go +++ b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go @@ -864,7 +864,9 @@ func GetNextStakerChangeTime(state state.Chain) (time.Time, error) { case hasPendingStaker: return pendingStakerIterator.Value().NextTime, nil default: - return time.Time{}, database.ErrNotFound + // SGB-MERGE + // return time.Time{}, database.ErrNotFound + return state.GetTimestamp().Add(time.Second), nil } } diff --git a/coreth/consensus/dummy/consensus.go b/coreth/consensus/dummy/consensus.go index 3cdf4221..80aab076 100644 --- a/coreth/consensus/dummy/consensus.go +++ b/coreth/consensus/dummy/consensus.go @@ -96,20 +96,28 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade if header.GasUsed > header.GasLimit { return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) } - if config.IsApricotPhase1(timestamp) { - if header.GasLimit != params.ApricotPhase1GasLimit { - return fmt.Errorf("expected gas limit to be %d, but found %d", params.ApricotPhase1GasLimit, header.GasLimit) + if config.IsSongbirdCode() { + // SGB-MERGE + // Verify that the gas limit is correct for the current phase + if header.GasLimit != params.ApricotPhase5GasLimit { + return fmt.Errorf("expected gas limit to be %d in apricot phase 5 but got %d", params.ApricotPhase5GasLimit, header.GasLimit) } } else { - // Verify that the gas limit remains within allowed bounds - diff := int64(parent.GasLimit) - int64(header.GasLimit) - if diff < 0 { - diff *= -1 - } - limit := parent.GasLimit / params.GasLimitBoundDivisor - - if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit { - return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit) + if config.IsApricotPhase1(timestamp) { + if header.GasLimit != params.ApricotPhase1GasLimit { + return fmt.Errorf("expected gas limit to be %d, but found %d", params.ApricotPhase1GasLimit, header.GasLimit) + } + } else { + // Verify that the gas limit remains within allowed bounds + diff := int64(parent.GasLimit) - int64(header.GasLimit) + if diff < 0 { + diff *= -1 + } + limit := parent.GasLimit / params.GasLimitBoundDivisor + + if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit { + return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit) + } } } diff --git a/coreth/core/chain_makers.go b/coreth/core/chain_makers.go index f0c424b3..819d6f8e 100644 --- a/coreth/core/chain_makers.go +++ b/coreth/core/chain_makers.go @@ -290,10 +290,15 @@ func makeHeader(chain consensus.ChainReader, config *params.ChainConfig, parent timestamp := new(big.Int).SetUint64(time) var gasLimit uint64 - if config.IsApricotPhase1(timestamp) { - gasLimit = params.ApricotPhase1GasLimit + if config.IsSongbirdCode() { + // SGB-MERGE + gasLimit = params.ApricotPhase5GasLimit } else { - gasLimit = CalcGasLimit(parent.GasUsed(), parent.GasLimit(), parent.GasLimit(), parent.GasLimit()) + if config.IsApricotPhase1(timestamp) { + gasLimit = params.ApricotPhase1GasLimit + } else { + gasLimit = CalcGasLimit(parent.GasUsed(), parent.GasLimit(), parent.GasLimit(), parent.GasLimit()) + } } header := &types.Header{ diff --git a/coreth/miner/worker.go b/coreth/miner/worker.go index 4ce074cb..0e6f5299 100644 --- a/coreth/miner/worker.go +++ b/coreth/miner/worker.go @@ -128,12 +128,16 @@ func (w *worker) commitNewWork() (*types.Block, error) { } var gasLimit uint64 - if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { - gasLimit = params.ApricotPhase1GasLimit + if w.chainConfig.IsSongbirdCode() { + gasLimit = params.ApricotPhase5GasLimit } else { - // The gas limit is set in phase1 to ApricotPhase1GasLimit because the ceiling and floor were set to the same value - // such that the gas limit converged to it. Since this is hardbaked now, we remove the ability to configure it. - gasLimit = core.CalcGasLimit(parent.GasUsed(), parent.GasLimit(), params.ApricotPhase1GasLimit, params.ApricotPhase1GasLimit) + if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { + gasLimit = params.ApricotPhase1GasLimit + } else { + // The gas limit is set in phase1 to ApricotPhase1GasLimit because the ceiling and floor were set to the same value + // such that the gas limit converged to it. Since this is hardbaked now, we remove the ability to configure it. + gasLimit = core.CalcGasLimit(parent.GasUsed(), parent.GasLimit(), params.ApricotPhase1GasLimit, params.ApricotPhase1GasLimit) + } } num := parent.Number() header := &types.Header{ diff --git a/coreth/params/avalanche_params.go b/coreth/params/avalanche_params.go index 526ebbb8..eb47f134 100644 --- a/coreth/params/avalanche_params.go +++ b/coreth/params/avalanche_params.go @@ -20,6 +20,9 @@ const ( ApricotPhase1GasLimit uint64 = 8_000_000 + // SGB-MERGE + ApricotPhase5GasLimit uint64 = 30_000_000 + ApricotPhase3ExtraDataSize uint64 = 80 ApricotPhase3MinBaseFee int64 = 75_000_000_000 ApricotPhase3MaxBaseFee int64 = 225_000_000_000 diff --git a/coreth/params/config.go b/coreth/params/config.go index 0756749c..53060010 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -48,10 +48,12 @@ var ( // Flare Chain IDs. FlareChainID = big.NewInt(14) // https://github.com/ethereum-lists/chains/blob/master/_data/chains/eip155-14.json + CostonChainID = big.NewInt(16) // https://github.com/ethereum-lists/chains/blob/master/_data/chains/eip155-16.json SongbirdChainID = big.NewInt(19) // https://github.com/ethereum-lists/chains/blob/master/_data/chains/eip155-19.json CostwoChainID = big.NewInt(114) // TO-DO: Register with https://github.com/ethereum-lists StagingChainID = big.NewInt(161) LocalFlareChainID = big.NewInt(162) + LocalChainID = big.NewInt(4294967295) errNonGenesisForkByHeight = errors.New("coreth only supports forking by height at the genesis block") ) @@ -213,6 +215,12 @@ func (c *ChainConfig) String() string { return banner } +// SGB-MERGE +// Code for songbird network (songbird, coston, local id) +func (c *ChainConfig) IsSongbirdCode() bool { + return c.ChainID.Cmp(SongbirdChainID) == 0 || c.ChainID.Cmp(CostonChainID) == 0 || c.ChainID.Cmp(LocalChainID) == 0 +} + // IsHomestead returns whether num is either equal to the homestead block or greater. func (c *ChainConfig) IsHomestead(num *big.Int) bool { return utils.IsForked(c.HomesteadBlock, num) @@ -526,6 +534,10 @@ type Rules struct { IsApricotPhase1, IsApricotPhase2, IsApricotPhase3, IsApricotPhase4, IsApricotPhase5 bool IsBlueberry bool + // SGB-MERGE + // Songbird (coston, local) + IsSongbirdCode bool + // Precompiles maps addresses to stateful precompiled contracts that are enabled // for this rule set. // Note: none of these addresses should conflict with the address space used by @@ -549,6 +561,7 @@ func (c *ChainConfig) rules(num *big.Int) Rules { IsConstantinople: c.IsConstantinople(num), IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), + IsSongbirdCode: c.IsSongbirdCode(), } } diff --git a/coreth/plugin/evm/block_verification.go b/coreth/plugin/evm/block_verification.go index e192da2a..0a2ad942 100644 --- a/coreth/plugin/evm/block_verification.go +++ b/coreth/plugin/evm/block_verification.go @@ -8,6 +8,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" safemath "github.com/ava-labs/avalanchego/utils/math" @@ -98,13 +99,29 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { } // Enforce static gas limit after ApricotPhase1 (prior to ApricotPhase1 it's handled in processing). - if rules.IsApricotPhase1 { - if ethHeader.GasLimit != params.ApricotPhase1GasLimit { + + // DEBUG !!!!! + log.Info(fmt.Sprintf("DEBUG: ethHeader.GasLimit: %d", ethHeader.GasLimit)) + log.Info(fmt.Sprintf("DEBUG IsSongbirdCode: %t", rules.IsSongbirdCode)) + log.Info(fmt.Sprintf("DEBUG chain id: %v", rules.ChainID.Int64())) + + if rules.IsSongbirdCode { + // SGB-MERGE + if ethHeader.GasLimit != params.ApricotPhase5GasLimit { return fmt.Errorf( - "expected gas limit to be %d after apricot phase 1 but got %d", - params.ApricotPhase1GasLimit, ethHeader.GasLimit, + "expected gas limit to be %d in apricot phase 5 but got %d", + params.ApricotPhase5GasLimit, ethHeader.GasLimit, ) } + } else { + if rules.IsApricotPhase1 { + if ethHeader.GasLimit != params.ApricotPhase1GasLimit { + return fmt.Errorf( + "expected gas limit to be %d after apricot phase 1 but got %d", + params.ApricotPhase1GasLimit, ethHeader.GasLimit, + ) + } + } } // Check that the size of the header's Extra data field is correct for [rules]. From 8b4b5ba25fabfd0b8e1fb586c3b01cbd0cf9deb8 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 11 Mar 2024 17:08:10 +0100 Subject: [PATCH 10/39] Default validator handling, fixed tests --- avalanchego/genesis/genesis_local.go | 5 ++ avalanchego/genesis/genesis_test.go | 84 +++++++++---------- avalanchego/node/node.go | 4 +- avalanchego/snow/validators/custom.go | 83 ++++++++++++++++-- avalanchego/snow/validators/manager.go | 14 +--- avalanchego/utils/constants/network_ids.go | 15 ++-- .../utils/constants/network_ids_test.go | 16 ++-- .../platformvm/blocks/builder/builder_test.go | 2 + avalanchego/vms/platformvm/state/diff.go | 8 ++ avalanchego/vms/platformvm/state/mock_diff.go | 4 + .../vms/platformvm/state/mock_state.go | 4 + avalanchego/vms/platformvm/state/state.go | 20 +++++ .../txs/executor/advance_time_test.go | 2 + .../txs/executor/proposal_tx_executor.go | 6 +- avalanchego/vms/platformvm/vm.go | 12 +++ 15 files changed, 199 insertions(+), 80 deletions(-) diff --git a/avalanchego/genesis/genesis_local.go b/avalanchego/genesis/genesis_local.go index 766eca44..6086efc0 100644 --- a/avalanchego/genesis/genesis_local.go +++ b/avalanchego/genesis/genesis_local.go @@ -6,12 +6,17 @@ package genesis import ( "time" + "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/platformvm/reward" // "github.com/flare-foundation/flare/utils/units" // "github.com/flare-foundation/flare/vms/platformvm/reward" ) +var ( + EWOQKey *crypto.PrivateKeySECP256K1R +) + var ( localGenesisConfigJSON = `{ "networkID": 12345, diff --git a/avalanchego/genesis/genesis_test.go b/avalanchego/genesis/genesis_test.go index 1cb9bdfd..6a315f67 100644 --- a/avalanchego/genesis/genesis_test.go +++ b/avalanchego/genesis/genesis_test.go @@ -44,8 +44,8 @@ func TestValidateConfig(t *testing.T) { config: &FujiConfig, }, "local": { - networkID: 12345, - config: &LocalConfig, + networkID: 162, + config: &LocalFlareConfig, }, "mainnet (networkID mismatch)": { networkID: 2, @@ -53,63 +53,63 @@ func TestValidateConfig(t *testing.T) { err: "networkID 2 specified but genesis config contains networkID 1", }, "invalid start time": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.StartTime = 999999999999999 return &thisConfig }(), err: "start time cannot be in the future", }, "no initial supply": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.Allocations = []Allocation{} return &thisConfig }(), err: "initial supply must be > 0", }, "no initial stakers": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.InitialStakers = []Staker{} return &thisConfig }(), err: "initial stakers must be > 0", }, "invalid initial stake duration": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.InitialStakeDuration = 0 return &thisConfig }(), err: "initial stake duration must be > 0", }, "invalid stake offset": { - networkID: 12345, + networkID: 14, config: func() *Config { - thisConfig := LocalConfig + thisConfig := FlareConfig thisConfig.InitialStakeDurationOffset = 100000000 return &thisConfig }(), - err: "initial stake duration is 31536000 but need at least 400000000 with offset of 100000000", + err: "initial stake duration is 31536000 but need at least 1900000000 with offset of 100000000", }, "empty initial staked funds": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.InitialStakedFunds = []ids.ShortID(nil) return &thisConfig }(), err: "initial staked funds cannot be empty", }, "duplicate initial staked funds": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.InitialStakedFunds = append(thisConfig.InitialStakedFunds, thisConfig.InitialStakedFunds[0]) return &thisConfig }(), @@ -119,24 +119,24 @@ func TestValidateConfig(t *testing.T) { networkID: 5, config: func() *Config { thisConfig := FujiConfig - thisConfig.InitialStakedFunds = append(thisConfig.InitialStakedFunds, LocalConfig.InitialStakedFunds[0]) + thisConfig.InitialStakedFunds = append(thisConfig.InitialStakedFunds, LocalFlareConfig.InitialStakedFunds[0]) return &thisConfig }(), err: "does not have an allocation to stake", }, "empty C-Chain genesis": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.CChainGenesis = "" return &thisConfig }(), err: "C-Chain genesis cannot be empty", }, "empty message": { - networkID: 12345, + networkID: 162, config: func() *Config { - thisConfig := LocalConfig + thisConfig := LocalFlareConfig thisConfig.Message = "" return &thisConfig }(), @@ -171,15 +171,15 @@ func TestGenesisFromFile(t *testing.T) { customConfig: customGenesisConfigJSON, err: "cannot override genesis config for standard network mainnet (1)", }, - "fuji": { - networkID: constants.FujiID, + "songbird": { + networkID: constants.SongbirdID, customConfig: customGenesisConfigJSON, - err: "cannot override genesis config for standard network fuji (5)", + err: "cannot override genesis config for standard network songbird (5)", }, - "fuji (with custom specified)": { - networkID: constants.FujiID, + "songbird (with custom specified)": { + networkID: constants.SongbirdID, customConfig: []byte(localGenesisConfigJSON), // won't load - err: "cannot override genesis config for standard network fuji (5)", + err: "cannot override genesis config for standard network songbird (5)", }, "local": { networkID: constants.LocalID, @@ -256,9 +256,9 @@ func TestGenesisFromFlag(t *testing.T) { networkID: constants.MainnetID, err: "cannot override genesis config for standard network mainnet (1)", }, - "fuji": { - networkID: constants.FujiID, - err: "cannot override genesis config for standard network fuji (5)", + "songbird": { + networkID: constants.SongbirdID, + err: "cannot override genesis config for standard network songbird (5)", }, "local": { networkID: constants.LocalID, @@ -344,12 +344,12 @@ func TestGenesis(t *testing.T) { expectedID: "UUvXi6j7QhVvgpbKM89MP5HdrxKm9CaJeHc187TsDNf8nZdLk", }, { - networkID: constants.FujiID, - expectedID: "MSj6o9TpezwsQx4Tv7SHqpVvCbJ8of1ikjsqPZ1bKRjc9zBy3", + networkID: constants.SongbirdID, + expectedID: "wehzwSstW6ChWVj356tLr6sJfSb8PaZMhcerXUyxsAUzia1Gr", }, { networkID: constants.LocalID, - expectedID: "hBbtNFKLpjuKti32L5bnfZ2vspABkN268t8FincYhQWnWLHxw", + expectedID: "pA6uxpovoxuKFwNxGndoX9YTYDUWCFnqwDodVWvS43UWT6Zde", }, } for _, test := range tests { @@ -389,15 +389,15 @@ func TestVMGenesis(t *testing.T) { }, }, { - networkID: constants.FujiID, + networkID: constants.SongbirdID, vmTest: []vmTest{ { vmID: constants.AVMID, - expectedID: "2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm", + expectedID: "7xKYhEvYuUekwDxozgEiMPufzJ3jJPypKbGE8ny6KL84z4RKB", }, { vmID: constants.EVMID, - expectedID: "yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp", + expectedID: "2aamPVJ7SXz7DBUxFNY1Lyga52KWaVUH2Gz9Fetxo5gwjY5a29", }, }, }, @@ -406,11 +406,11 @@ func TestVMGenesis(t *testing.T) { vmTest: []vmTest{ { vmID: constants.AVMID, - expectedID: "2eNy1mUFdmaxXNj1eQHUe7Np4gju9sJsEtWQ4MX3ToiNKuADed", + expectedID: "ALRkp1tuy7ErVkWuEWFLVd657JAULWDDyQkQBkLKVE94jCaNu", }, { vmID: constants.EVMID, - expectedID: "2CA6j5zYzasynPsFeNoqWkmTCt3VScMvXUZHbfDJ8k3oGzAPtU", + expectedID: "yHEy62ti66aY6p4gzGWd2d5DCgSCuuYEnHJUagQVxPm24gz94", }, }, }, @@ -454,12 +454,12 @@ func TestAVAXAssetID(t *testing.T) { expectedID: "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", }, { - networkID: constants.FujiID, - expectedID: "U8iRqJoiJm8xZHAacmvYyZVwqQx6uDNtQeP3CQ6fcgQk3JqnK", + networkID: constants.SongbirdID, + expectedID: "1S3PSi4VsVpD8iK2vdykuajxVeuCV2xhjPSkQ4K88mqWGozMP", }, { networkID: constants.LocalID, - expectedID: "2fombhL7aGPwj3KH4bfrmJwW6PVnMobf9Y2fn9GwxiAAJyFDbe", + expectedID: "2RULRJVXVpQNAsV3sBpy4G8LWH1LN3z5Adokv5bVtnZmsBQDCX", }, } diff --git a/avalanchego/node/node.go b/avalanchego/node/node.go index 319a473e..33690f1c 100644 --- a/avalanchego/node/node.go +++ b/avalanchego/node/node.go @@ -727,7 +727,7 @@ func (n *Node) initVMs() error { // Instead of updating node's validator manager, platform chain makes changes // to its own local validator manager (which isn't used for sampling) if !n.Config.EnableStaking { - vdrs = validators.NewManager(n.Config.NetworkID) + vdrs = validators.NewManager() } vmRegisterer := registry.NewVMRegisterer(registry.VMRegistererConfig{ @@ -1120,7 +1120,7 @@ func (n *Node) initAPIAliases(genesisBytes []byte) error { // Initializes [n.vdrs] and returns the Primary Network validator set. func (n *Node) initVdrs() (validators.Set, error) { - n.vdrs = validators.NewManager(n.Config.NetworkID) + n.vdrs = validators.NewManager() vdrSet := validators.NewSet() if err := n.vdrs.Set(constants.PrimaryNetworkID, vdrSet); err != nil { return vdrSet, fmt.Errorf("couldn't set primary network validators: %w", err) diff --git a/avalanchego/snow/validators/custom.go b/avalanchego/snow/validators/custom.go index 51ca96b9..3602502a 100644 --- a/avalanchego/snow/validators/custom.go +++ b/avalanchego/snow/validators/custom.go @@ -1,11 +1,15 @@ package validators +// SGB-MERGE + import ( + "errors" "fmt" "os" "strings" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" ) const ( @@ -13,8 +17,59 @@ const ( customValidatorEnv = "CUSTOM_VALIDATORS" ) -func loadCustomValidators() Set { - set := NewSet() +var ( + defaultValidators = defaultValidatorSet{} + errNotInitialized = errors.New("default validator set not initialized") +) + +type defaultValidatorSet struct { + initialzed bool + vdrMap map[ids.NodeID]Validator +} + +func (dvs *defaultValidatorSet) initialize(networkID uint32) { + if dvs.initialzed { + return + } + + var vdrs []Validator + switch networkID { + case constants.LocalID: + vdrs = loadCustomValidators() + case constants.SongbirdID: + // Todo: Add songbird network validators + vdrs = []Validator{} + case constants.CostonID: + // Todo: Add coston network validators + vdrs = []Validator{} + } + dvs.vdrMap = make(map[ids.NodeID]Validator) + for _, vdr := range vdrs { + dvs.vdrMap[vdr.ID()] = vdr + } + dvs.initialzed = true +} + +func (dvs *defaultValidatorSet) list() []Validator { + if !dvs.initialzed { + panic(errNotInitialized) + } + vdrs := make([]Validator, 0, len(dvs.vdrMap)) + for _, vdr := range dvs.vdrMap { + vdrs = append(vdrs, vdr) + } + return vdrs +} + +func (dvs *defaultValidatorSet) isValidator(vdrID ids.NodeID) bool { + if !dvs.initialzed { + panic(errNotInitialized) + } + _, ok := dvs.vdrMap[vdrID] + return ok +} + +func loadCustomValidators() (vdrs []Validator) { weight := uint64(customValidatorWeight) customValidatorList := os.Getenv(customValidatorEnv) nodeIDs := strings.Split(customValidatorList, ",") @@ -24,12 +79,24 @@ func loadCustomValidators() Set { } shortID, err := ids.ShortFromPrefixedString(nodeID, ids.NodeIDPrefix) if err != nil { - panic(fmt.Sprintf("invalid custom validator node ID: %s", nodeID)) - } - err = set.AddWeight(ids.NodeID(shortID), weight) - if err != nil { - panic(fmt.Sprintf("could not add weight for validator (node: %x, weight: %d): %s", shortID, weight, err)) + panic(fmt.Errorf("invalid custom validator node ID: %s", nodeID)) } + vdrs = append(vdrs, &validator{ + nodeID: ids.NodeID(shortID), + weight: weight, + }) } - return set + return +} + +func DefaultValidatorList() []Validator { + return defaultValidators.list() +} + +func IsDefaultValidator(vdrID ids.NodeID) bool { + return defaultValidators.isValidator(vdrID) +} + +func InitializeDefaultValidators(networkID uint32) { + defaultValidators.initialize(networkID) } diff --git a/avalanchego/snow/validators/manager.go b/avalanchego/snow/validators/manager.go index 15b5e044..0362ad2b 100644 --- a/avalanchego/snow/validators/manager.go +++ b/avalanchego/snow/validators/manager.go @@ -9,7 +9,6 @@ import ( "sync" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/constants" ) var _ Manager = &manager{} @@ -44,18 +43,9 @@ type Manager interface { } // NewManager returns a new, empty manager -// SGB-MERGE -func NewManager(networkID uint32) Manager { - subnetToVdrs := make(map[ids.ID]Set) - switch networkID { - case constants.LocalID: - subnetToVdrs[constants.PrimaryNetworkID] = loadCustomValidators() - // SGB-MERGE - // TODO: Add sgb and coston network validators - } - +func NewManager() Manager { return &manager{ - subnetToVdrs: subnetToVdrs, + subnetToVdrs: make(map[ids.ID]Set), } } diff --git a/avalanchego/utils/constants/network_ids.go b/avalanchego/utils/constants/network_ids.go index 1c0e7548..b5bcac95 100644 --- a/avalanchego/utils/constants/network_ids.go +++ b/avalanchego/utils/constants/network_ids.go @@ -94,8 +94,9 @@ var ( CostwoName: CostwoID, StagingName: StagingID, LocalFlareName: LocalFlareID, - SongbirdName: SongbirdID, - CostonName: CostonID, + // SGB-MERGE + SongbirdName: SongbirdID, + CostonName: CostonID, } NetworkIDToHRP = map[uint32]string{ @@ -109,8 +110,9 @@ var ( CostwoID: CostwoHRP, StagingID: StagingHRP, LocalFlareID: LocalFlareHRP, - SongbirdID: SongbirdHRP, - CostonID: CostonHRP, + // SGB-MERGE + SongbirdID: SongbirdHRP, + CostonID: CostonHRP, } NetworkHRPToNetworkID = map[string]uint32{ MainnetHRP: MainnetID, @@ -124,8 +126,9 @@ var ( CostwoHRP: CostwoID, StagingHRP: StagingID, LocalFlareHRP: LocalFlareID, - SongbirdHRP: SongbirdID, - CostonHRP: CostonID, + // SGB-MERGE + SongbirdHRP: SongbirdID, + CostonHRP: CostonID, } ValidNetworkPrefix = "network-" diff --git a/avalanchego/utils/constants/network_ids_test.go b/avalanchego/utils/constants/network_ids_test.go index 0536c13a..e85fb779 100644 --- a/avalanchego/utils/constants/network_ids_test.go +++ b/avalanchego/utils/constants/network_ids_test.go @@ -17,12 +17,12 @@ func TestGetHRP(t *testing.T) { hrp: MainnetHRP, }, { - id: TestnetID, - hrp: FujiHRP, + id: CostonID, + hrp: CostonHRP, }, { - id: FujiID, - hrp: FujiHRP, + id: SongbirdID, + hrp: SongbirdName, }, { id: LocalID, @@ -69,12 +69,12 @@ func TestNetworkName(t *testing.T) { name: MainnetName, }, { - id: TestnetID, - name: FujiName, + id: CostonID, + name: CostonName, }, { - id: FujiID, - name: FujiName, + id: SongbirdID, + name: SongbirdName, }, { id: LocalID, diff --git a/avalanchego/vms/platformvm/blocks/builder/builder_test.go b/avalanchego/vms/platformvm/blocks/builder/builder_test.go index a85baf08..e12c23c0 100644 --- a/avalanchego/vms/platformvm/blocks/builder/builder_test.go +++ b/avalanchego/vms/platformvm/blocks/builder/builder_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" "github.com/ava-labs/avalanchego/vms/platformvm/txs" @@ -20,6 +21,7 @@ import ( func TestBlockBuilderAddLocalTx(t *testing.T) { require := require.New(t) + validators.InitializeDefaultValidators(0) env := newEnvironment(t) env.ctx.Lock.Lock() defer func() { diff --git a/avalanchego/vms/platformvm/state/diff.go b/avalanchego/vms/platformvm/state/diff.go index 44ebd8da..9843e23a 100644 --- a/avalanchego/vms/platformvm/state/diff.go +++ b/avalanchego/vms/platformvm/state/diff.go @@ -28,6 +28,9 @@ type Diff interface { } type diff struct { + // SGB-MERGE + networkID uint32 + parentID ids.ID stateVersions Versions @@ -68,6 +71,7 @@ func NewDiff( return nil, fmt.Errorf("%w: %s", ErrMissingParentState, parentID) } return &diff{ + networkID: parentState.GetNetworkID(), parentID: parentID, stateVersions: stateVersions, timestamp: parentState.GetTimestamp(), @@ -75,6 +79,10 @@ func NewDiff( }, nil } +func (d *diff) GetNetworkID() uint32 { + return d.networkID +} + func (d *diff) GetTimestamp() time.Time { return d.timestamp } diff --git a/avalanchego/vms/platformvm/state/mock_diff.go b/avalanchego/vms/platformvm/state/mock_diff.go index 643ebe2e..3356a797 100644 --- a/avalanchego/vms/platformvm/state/mock_diff.go +++ b/avalanchego/vms/platformvm/state/mock_diff.go @@ -44,6 +44,10 @@ func (m *MockDiff) AddChain(createChainTx *txs.Tx) { m.ctrl.Call(m, "AddChain", createChainTx) } +func (m *MockDiff) GetNetworkID() uint32 { + return 0 +} + // AddChain indicates an expected call of AddChain. func (mr *MockDiffMockRecorder) AddChain(createChainTx interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() diff --git a/avalanchego/vms/platformvm/state/mock_state.go b/avalanchego/vms/platformvm/state/mock_state.go index 4d450f4b..e1cf5123 100644 --- a/avalanchego/vms/platformvm/state/mock_state.go +++ b/avalanchego/vms/platformvm/state/mock_state.go @@ -563,6 +563,10 @@ func (mr *MockStateMockRecorder) Abort() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Abort", reflect.TypeOf((*MockState)(nil).Abort)) } +func (m *MockState) GetNetworkID() uint32 { + return 0 +} + // AddChain mocks base method. func (m *MockState) AddChain(createChainTx *txs.Tx) { m.ctrl.T.Helper() diff --git a/avalanchego/vms/platformvm/state/state.go b/avalanchego/vms/platformvm/state/state.go index 303a3f88..881cd8b4 100644 --- a/avalanchego/vms/platformvm/state/state.go +++ b/avalanchego/vms/platformvm/state/state.go @@ -80,6 +80,9 @@ type Chain interface { UTXOGetter UTXODeleter + // SGB-MERGE + GetNetworkID() uint32 + GetTimestamp() time.Time SetTimestamp(tm time.Time) GetCurrentSupply() uint64 @@ -330,6 +333,14 @@ func New( return nil, err } + // SGB-MERGE set uptimes of default validators + for _, vdr := range validators.DefaultValidatorList() { + s.uptimes[vdr.ID()] = &uptimeAndReward{ + txID: ids.Empty, + lastUpdated: s.GetTimestamp(), + } + } + return s, nil } @@ -479,6 +490,10 @@ func new( }, nil } +func (s *state) GetNetworkID() uint32 { + return s.ctx.NetworkID +} + func (s *state) GetCurrentValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { return s.currentStakers.GetValidator(subnetID, nodeID) } @@ -1602,6 +1617,11 @@ func (s *state) writeUptimes() error { for nodeID := range s.updatedUptimes { delete(s.updatedUptimes, nodeID) + // SGB-MERGE skip default validator + if validators.IsDefaultValidator(nodeID) { + continue + } + uptime := s.uptimes[nodeID] uptime.LastUpdated = uint64(uptime.lastUpdated.Unix()) diff --git a/avalanchego/vms/platformvm/txs/executor/advance_time_test.go b/avalanchego/vms/platformvm/txs/executor/advance_time_test.go index 68592d7b..3dfc723a 100644 --- a/avalanchego/vms/platformvm/txs/executor/advance_time_test.go +++ b/avalanchego/vms/platformvm/txs/executor/advance_time_test.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/vms/platformvm/reward" @@ -22,6 +23,7 @@ import ( // Ensure semantic verification fails when proposed timestamp is at or before current timestamp func TestAdvanceTimeTxTimestampTooEarly(t *testing.T) { + validators.InitializeDefaultValidators(0) env := newEnvironment() defer func() { if err := shutdownEnvironment(env); err != nil { diff --git a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go index be939d85..d93a0953 100644 --- a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go +++ b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go @@ -865,8 +865,10 @@ func GetNextStakerChangeTime(state state.Chain) (time.Time, error) { return pendingStakerIterator.Value().NextTime, nil default: // SGB-MERGE - // return time.Time{}, database.ErrNotFound - return state.GetTimestamp().Add(time.Second), nil + if state.GetNetworkID() == constants.SongbirdID || state.GetNetworkID() == constants.CostonID || state.GetNetworkID() == constants.LocalID { + return state.GetTimestamp().Add(time.Second), nil + } + return time.Time{}, database.ErrNotFound } } diff --git a/avalanchego/vms/platformvm/vm.go b/avalanchego/vms/platformvm/vm.go index d8fa29ff..70b23cb9 100644 --- a/avalanchego/vms/platformvm/vm.go +++ b/avalanchego/vms/platformvm/vm.go @@ -119,6 +119,9 @@ func (vm *VM) Initialize( ) error { ctx.Log.Verbo("initializing platform chain") + // SGB-MERGE: This is a new line of code + validators.InitializeDefaultValidators(ctx.NetworkID) + registerer := prometheus.NewRegistry() if err := ctx.Metrics.Register(registerer); err != nil { return err @@ -569,6 +572,15 @@ func (vm *VM) updateValidators() error { if err != nil { return err } + + // SGB-MERGE: This are new lines of code + for _, v := range validators.DefaultValidatorList() { + err := primaryValidators.AddWeight(v.ID(), v.Weight()) + if err != nil { + return err + } + } + if err := vm.Validators.Set(constants.PrimaryNetworkID, primaryValidators); err != nil { return err } From fe1ceb9f8429c55253b6fa52d844b12f1344c9e6 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 20 Mar 2024 20:17:49 +0100 Subject: [PATCH 11/39] Merged songbird differences to state transition --- avalanchego/scripts/build.sh | 2 + avalanchego/snow/validators/custom.go | 83 ++++++++++++---- config/localflare/C/config.json | 32 +++++- coreth/core/daemon.go | 95 ++++++++++++------ coreth/core/daemon_test.go | 13 +-- coreth/core/state_connector.go | 132 ++++++++++++++++++++----- coreth/core/state_transition.go | 111 ++++++++++++--------- coreth/core/state_transition_params.go | 38 +++++++ coreth/utils/flare_choice.go | 34 +++++++ 9 files changed, 408 insertions(+), 132 deletions(-) create mode 100644 coreth/core/state_transition_params.go create mode 100644 coreth/utils/flare_choice.go diff --git a/avalanchego/scripts/build.sh b/avalanchego/scripts/build.sh index 9c0c6cf0..5baef0f9 100755 --- a/avalanchego/scripts/build.sh +++ b/avalanchego/scripts/build.sh @@ -16,6 +16,8 @@ source "$AVALANCHE_PATH"/scripts/constants.sh echo "Downloading dependencies..." go mod download -modcacherw +echo "Syncing with sources at GOPATH: $GOPATH" + rsync -ar --delete $AVALANCHE_PATH/* $GOPATH/pkg/mod/github.com/ava-labs/avalanchego@$avalanche_version rsync -ar --delete $CORETH_PATH/* $GOPATH/pkg/mod/github.com/ava-labs/coreth@$coreth_version diff --git a/avalanchego/snow/validators/custom.go b/avalanchego/snow/validators/custom.go index 3602502a..c37c089b 100644 --- a/avalanchego/snow/validators/custom.go +++ b/avalanchego/snow/validators/custom.go @@ -13,8 +13,10 @@ import ( ) const ( - customValidatorWeight = 200_000 - customValidatorEnv = "CUSTOM_VALIDATORS" + songbirdValidatorWeight = 50_000 + costonValidatorWeight = 200_000 + customValidatorWeight = 200_000 + customValidatorEnv = "CUSTOM_VALIDATORS" ) var ( @@ -22,6 +24,18 @@ var ( errNotInitialized = errors.New("default validator set not initialized") ) +func DefaultValidatorList() []Validator { + return defaultValidators.list() +} + +func IsDefaultValidator(vdrID ids.NodeID) bool { + return defaultValidators.isValidator(vdrID) +} + +func InitializeDefaultValidators(networkID uint32) { + defaultValidators.initialize(networkID) +} + type defaultValidatorSet struct { initialzed bool vdrMap map[ids.NodeID]Validator @@ -37,11 +51,9 @@ func (dvs *defaultValidatorSet) initialize(networkID uint32) { case constants.LocalID: vdrs = loadCustomValidators() case constants.SongbirdID: - // Todo: Add songbird network validators - vdrs = []Validator{} + vdrs = loadSongbirdValidators() case constants.CostonID: - // Todo: Add coston network validators - vdrs = []Validator{} + vdrs = loadCostonValidators() } dvs.vdrMap = make(map[ids.NodeID]Validator) for _, vdr := range vdrs { @@ -69,17 +81,58 @@ func (dvs *defaultValidatorSet) isValidator(vdrID ids.NodeID) bool { return ok } -func loadCustomValidators() (vdrs []Validator) { - weight := uint64(customValidatorWeight) +func loadCustomValidators() []Validator { customValidatorList := os.Getenv(customValidatorEnv) nodeIDs := strings.Split(customValidatorList, ",") + return createValidators(nodeIDs, uint64(customValidatorWeight)) +} + +func loadCostonValidators() []Validator { + nodeIDs := []string{ + "NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc", + "NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ", + "NodeID-FPAwqHjs8Mw8Cuki5bkm3vSVisZr8t2Lu", + "NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R", + "NodeID-HaZ4HpanjndqSuN252chFsTysmdND5meA", + } + return createValidators(nodeIDs, uint64(costonValidatorWeight)) +} + +func loadSongbirdValidators() []Validator { + nodeIDs := []string{ + "NodeID-3M9KVT6ixi4gVMisbm5TnPXYXgFN5LHuv", + "NodeID-NnX4fajAmyvpL9RLfheNdc47FKKDuQW8i", + "NodeID-AzdF8JNU468uwZYGquHt7bhDrsggZpK67", + "NodeID-FqeGcnLAXbDTthd382aP9uyu1i47paRRh", + "NodeID-B9HuZ5hDkRodyRRsiMEHWgMmmMF7xSKbj", + "NodeID-Jx3E1F7mfkseZmqnFgDUFV3eusMxVdT6Z", + "NodeID-FnvWuwvJGezs4uaBLujkfeM8U3gmAUY3Z", + "NodeID-LhVs6hzHjBcEkzA1Eu8Qxb9nEQAk1Qbgf", + "NodeID-9SqDo3MxpvEDN4bE4rLTyM7HkkKAw4h96", + "NodeID-4tStYRTi3KDxFmv1YHTZAQxbzeyMA7z52", + "NodeID-8XnMh17zo6pB8Pa2zptRBi9TbbMZgij2t", + "NodeID-Cn9P5wgg7d9RNLqm4dFLCUV2diCxpkj7f", + "NodeID-PEDdah7g7Efiii1xw8ex2dH58oMfByzjb", + "NodeID-QCt9AxMPt5nn445CQGoA3yktqkChnKmPY", + "NodeID-9bWz6J61B8WbQtzeSyA1jsXosyVbuUJd1", + "NodeID-DLMnewsEwtSH8Qk7p9RGzUVyZAaZVMKsk", + "NodeID-7meEpyjmGbL577th58dm4nvvtVZiJusFp", + "NodeID-JeYnnrUkuArAAe2Sjo47Z3X5yfeF7cw43", + "NodeID-Fdwp9Wtjh5rxzuTCF9z4zrSM31y7ZzBQS", + "NodeID-JdEBRLS98PansyFKQUzFKqk4xqrVZ41nC", + } + return createValidators(nodeIDs, uint64(songbirdValidatorWeight)) +} + +func createValidators(nodeIDs []string, weight uint64) (vdrs []Validator) { for _, nodeID := range nodeIDs { if nodeID == "" { continue } + shortID, err := ids.ShortFromPrefixedString(nodeID, ids.NodeIDPrefix) if err != nil { - panic(fmt.Errorf("invalid custom validator node ID: %s", nodeID)) + panic(fmt.Sprintf("invalid validator node ID: %s", nodeID)) } vdrs = append(vdrs, &validator{ nodeID: ids.NodeID(shortID), @@ -88,15 +141,3 @@ func loadCustomValidators() (vdrs []Validator) { } return } - -func DefaultValidatorList() []Validator { - return defaultValidators.list() -} - -func IsDefaultValidator(vdrID ids.NodeID) bool { - return defaultValidators.isValidator(vdrID) -} - -func InitializeDefaultValidators(networkID uint32) { - defaultValidators.initialize(networkID) -} diff --git a/config/localflare/C/config.json b/config/localflare/C/config.json index c6da0fc0..5edc0f04 100644 --- a/config/localflare/C/config.json +++ b/config/localflare/C/config.json @@ -18,5 +18,33 @@ "internal-debug", "internal-account", "internal-personal" - ] - } \ No newline at end of file + ], + "continuous-profiler-dir": "", + "continuous-profiler-frequency": 900000000000, + "continuous-profiler-max-files": 5, + "rpc-gas-cap": 50000000, + "rpc-tx-fee-cap": 100, + "preimages-enabled": false, + "pruning-enabled": false, + "snapshot-async": true, + "snapshot-verification-enabled": false, + "metrics-enabled": true, + "metrics-expensive-enabled": false, + "local-txs-enabled": false, + "api-max-duration": 30000000000, + "ws-cpu-refill-rate": 0, + "ws-cpu-max-stored": 0, + "api-max-blocks-per-request": 30, + "allow-unfinalized-queries": false, + "allow-unprotected-txs": false, + "keystore-directory": "", + "keystore-external-signer": "", + "keystore-insecure-unlock-allowed": false, + "remote-tx-gossip-only-enabled": false, + "tx-regossip-frequency": 60000000000, + "tx-regossip-max-size": 15, + "log-level": "info", + "offline-pruning-enabled": false, + "offline-pruning-bloom-filter-size": 512, + "offline-pruning-data-directory": "" +} diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index e69a799d..b010ffa4 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -5,6 +5,7 @@ package core import ( "fmt" + "math" "math/big" "os" "time" @@ -14,6 +15,7 @@ import ( "github.com/ava-labs/coreth/core/vm" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/utils" ) var ( @@ -21,11 +23,61 @@ var ( submitterContractActivationTimeFlare = big.NewInt(time.Date(2024, time.March, 26, 12, 0, 0, 0, time.UTC).Unix()) submitterContractActivationTimeCostwo = big.NewInt(time.Date(2024, time.February, 21, 14, 0, 0, 0, time.UTC).Unix()) + submitterContractActivationTimeSongbird = big.NewInt(time.Date(2024, time.March, 15, 12, 0, 0, 0, time.UTC).Unix()) + submitterContractActivationTimeCoston = big.NewInt(time.Date(2024, time.February, 29, 12, 0, 0, 0, time.UTC).Unix()) + // Define ftso and submitter contract addresses prioritisedFTSOContractAddress = common.HexToAddress("0x1000000000000000000000000000000000000003") - prioritisedSubmitterContractAddress = common.HexToAddress("0x2cA6571Daa15ce734Bbd0Bf27D5C9D16787fc33f") - prioritisedSubmitterContractAddressEnv = common.HexToAddress(os.Getenv("SUBMITTER_CONTRACT_ADDRESS")) // for local and staging chains + prioritisedSubmitterContractAddress = common.HexToAddress("0x2cA6571Daa15ce734Bbd0Bf27D5C9D16787fc33f") // for flare, costwo, songbird and coston + prioritisedSubmitterContractAddressEnv = common.HexToAddress(os.Getenv("SUBMITTER_CONTRACT_ADDRESS")) // for local and staging chains +) + +type prioritisedParams struct { + submitterActivationTime *big.Int + submitterAddress common.Address + maxGasLimit uint64 +} + +var ( + prioritisedContractVariants = utils.NewChainValue(&prioritisedParams{ + big.NewInt(0), common.Address{}, 0, + }). + AddValue(params.FlareChainID, &prioritisedParams{ + submitterContractActivationTimeFlare, + prioritisedSubmitterContractAddress, + 3000000, + }). + AddValue(params.CostwoChainID, &prioritisedParams{ + submitterContractActivationTimeCostwo, + prioritisedSubmitterContractAddress, + 3000000, + }). + AddValue(params.SongbirdChainID, &prioritisedParams{ + submitterContractActivationTimeSongbird, + prioritisedSubmitterContractAddress, + math.MaxUint64, + }). + AddValue(params.CostonChainID, &prioritisedParams{ + submitterContractActivationTimeCoston, + prioritisedSubmitterContractAddress, + math.MaxUint64, + }). + AddValue(params.LocalFlareChainID, &prioritisedParams{ + big.NewInt(0), + prioritisedSubmitterContractAddressEnv, + 3000000, + }). + AddValue(params.StagingChainID, &prioritisedParams{ + big.NewInt(0), + prioritisedSubmitterContractAddressEnv, + 3000000, + }). + AddValue(params.LocalChainID, &prioritisedParams{ + big.NewInt(0), + prioritisedSubmitterContractAddressEnv, + math.MaxUint64, + }) ) // Define errors @@ -59,14 +111,6 @@ type EVMCaller interface { AddBalance(addr common.Address, amount *big.Int) } -// Define maximums that can change by block time -func GetMaxFTSOGasLimit(blockTime *big.Int) uint64 { - switch { - default: - return 3000000 - } -} - func GetDaemonGasMultiplier(blockTime *big.Int) uint64 { switch { default: @@ -88,33 +132,20 @@ func GetDaemonSelector(blockTime *big.Int) []byte { } } -func isPrioritisedFTSOContract(to *common.Address) bool { - return to != nil && *to == prioritisedFTSOContractAddress -} - -func isPrioritisedSubmitterContract(chainID *big.Int, to *common.Address, blockTime *big.Int) bool { - switch { - case to == nil || chainID == nil || blockTime == nil: - return false - case chainID.Cmp(params.FlareChainID) == 0: - return *to == prioritisedSubmitterContractAddress && - blockTime.Cmp(submitterContractActivationTimeFlare) > 0 - case chainID.Cmp(params.CostwoChainID) == 0: - return *to == prioritisedSubmitterContractAddress && - blockTime.Cmp(submitterContractActivationTimeCostwo) > 0 - case chainID.Cmp(params.LocalFlareChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0: - return *to == prioritisedSubmitterContractAddressEnv - default: +func IsPrioritisedContractCall(chainID *big.Int, blockTime *big.Int, to *common.Address, ret []byte, initialGas uint64) bool { + if to == nil || chainID == nil || blockTime == nil { return false } -} -func IsPrioritisedContractCall(chainID *big.Int, to *common.Address, ret []byte, blockTime *big.Int) bool { + chainValue := prioritisedContractVariants.GetValue(chainID) + switch { - case isPrioritisedFTSOContract(to): + case initialGas > chainValue.maxGasLimit: + return false + case *to == prioritisedFTSOContractAddress: + return true + case *to == chainValue.submitterAddress && blockTime.Cmp(chainValue.submitterActivationTime) > 0 && !isZeroSlice(ret): return true - case isPrioritisedSubmitterContract(chainID, to, blockTime): - return !isZeroSlice(ret) default: return false } diff --git a/coreth/core/daemon_test.go b/coreth/core/daemon_test.go index 7be96a2f..756c8e13 100644 --- a/coreth/core/daemon_test.go +++ b/coreth/core/daemon_test.go @@ -483,26 +483,27 @@ func TestPrioritisedContract(t *testing.T) { address := common.HexToAddress("0x123456789aBCdEF123456789aBCdef123456789A") preForkTime := big.NewInt(time.Date(2024, time.March, 20, 12, 0, 0, 0, time.UTC).Unix()) postForkTime := big.NewInt(time.Date(2024, time.March, 27, 12, 0, 0, 0, time.UTC).Unix()) + initialGas := uint64(0) ret0 := [32]byte{} ret1 := [32]byte{} ret1[31] = 1 - if IsPrioritisedContractCall(params.FlareChainID, &address, nil, preForkTime) { + if IsPrioritisedContractCall(params.FlareChainID, preForkTime, &address, nil, initialGas) { t.Errorf("Expected false for wrong address") } - if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedFTSOContractAddress, nil, preForkTime) { + if !IsPrioritisedContractCall(params.FlareChainID, preForkTime, &prioritisedFTSOContractAddress, nil, initialGas) { t.Errorf("Expected true for FTSO contract") } - if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret1[:], preForkTime) { + if IsPrioritisedContractCall(params.FlareChainID, preForkTime, &prioritisedSubmitterContractAddress, ret1[:], initialGas) { t.Errorf("Expected false for submitter contract before activation") } - if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret1[:], postForkTime) { + if !IsPrioritisedContractCall(params.FlareChainID, postForkTime, &prioritisedSubmitterContractAddress, ret1[:], initialGas) { t.Errorf("Expected true for submitter contract after activation") } - if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret0[:], postForkTime) { + if IsPrioritisedContractCall(params.FlareChainID, postForkTime, &prioritisedSubmitterContractAddress, ret0[:], initialGas) { t.Errorf("Expected false for submitter contract with wrong return value") } - if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, nil, postForkTime) { + if IsPrioritisedContractCall(params.FlareChainID, postForkTime, &prioritisedSubmitterContractAddress, nil, initialGas) { t.Errorf("Expected false for submitter contract with no return value") } } diff --git a/coreth/core/state_connector.go b/coreth/core/state_connector.go index 2792735b..e4e8f588 100644 --- a/coreth/core/state_connector.go +++ b/coreth/core/state_connector.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/coreth/core/vm" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/utils" ) const ( @@ -24,10 +25,15 @@ const ( var ( flareActivationTime = big.NewInt(time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC).Unix()) - songbirdActivationTime = big.NewInt(time.Date(2022, time.March, 28, 14, 0, 0, 0, time.UTC).Unix()) costwoActivationTime = big.NewInt(time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC).Unix()) stagingActivationTime = big.NewInt(time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC).Unix()) localFlareActivationTime = big.NewInt(time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC).Unix()) + + songbirdActivationTime = big.NewInt(time.Date(2022, time.March, 28, 14, 0, 0, 0, time.UTC).Unix()) + songbirdOct22ForkTime = big.NewInt(time.Date(2022, time.October, 19, 15, 0, 0, 0, time.UTC).Unix()) + + costonActivationTime = big.NewInt(time.Date(2022, time.February, 25, 17, 0, 0, 0, time.UTC).Unix()) + costonOct22ForkTime = big.NewInt(time.Date(2022, time.October, 6, 15, 0, 0, 0, time.UTC).Unix()) ) type AttestationVotes struct { @@ -38,33 +44,107 @@ type AttestationVotes struct { abstainedAttestors []common.Address } +var ( + stateConnectorActivationVariants = utils.NewChainValue(func(*big.Int, common.Address) bool { return false }). + AddValue(params.FlareChainID, GetStateConnectorIsActivatedAndCalledFlare). + AddValue(params.SongbirdChainID, GetStateConnectorIsActivatedAndCalledSongbird). + AddValue(params.CostwoChainID, GetStateConnectorIsActivatedAndCalledCostwo). + AddValue(params.CostonChainID, GetStateConnectorIsActivatedAndCalledCoston). + AddValue(params.StagingChainID, GetStateConnectorIsActivatedAndCalledStaging). + AddValue(params.LocalFlareChainID, GetStateConnectorIsActivatedAndCalledLocalFlare). + AddValue(params.LocalChainID, GetStateConnectorIsActivatedAndCalledLocal) +) + func GetStateConnectorIsActivatedAndCalled(chainID *big.Int, blockTime *big.Int, to common.Address) bool { + return stateConnectorActivationVariants.GetValue(chainID)(blockTime, to) + + // Move variants to functions for better readability + // + // switch { + // case chainID.Cmp(params.FlareChainID) == 0: + // switch { + // case blockTime.Cmp(flareActivationTime) >= 0: + // return to == common.HexToAddress("0x1000000000000000000000000000000000000001") + // } + // case chainID.Cmp(params.SongbirdChainID) == 0: + // switch { + // case blockTime.Cmp(songbirdOct22ForkTime) > 0: + // return to == common.HexToAddress("0x0c13aDA1C7143Cf0a0795FFaB93eEBb6FAD6e4e3") + // case blockTime.Cmp(songbirdActivationTime) > 0: + // return to == common.HexToAddress("0x3A1b3220527aBA427d1e13e4b4c48c31460B4d91") + // } + // case chainID.Cmp(params.CostwoChainID) == 0: + // switch { + // case blockTime.Cmp(costwoActivationTime) >= 0: + // default: + // return to == common.HexToAddress("0x1000000000000000000000000000000000000001") + // } + // case chainID.Cmp(params.CostonChainID) == 0: + // switch { + // case blockTime.Cmp(costonOct22ForkTime) > 0: + // return to == common.HexToAddress("0x0c13aDA1C7143Cf0a0795FFaB93eEBb6FAD6e4e3") + // case blockTime.Cmp(costonActivationTime) > 0: + // return to == common.HexToAddress("0x947c76694491d3fD67a73688003c4d36C8780A97") + // default: + // return false + // } + // case chainID.Cmp(params.StagingChainID) == 0: + // switch { + // case blockTime.Cmp(stagingActivationTime) >= 0: + // return to == common.HexToAddress("0x1000000000000000000000000000000000000001") + // } + // case chainID.Cmp(params.LocalFlareChainID) == 0: + // switch { + // case blockTime.Cmp(localFlareActivationTime) >= 0: + // return to == common.HexToAddress("0x1000000000000000000000000000000000000001") + // } + // case chainID.Cmp(params.LocalChainID) == 0: + // return to == common.HexToAddress("0x1000000000000000000000000000000000000001") + // } + // return false +} + +func GetStateConnectorIsActivatedAndCalledFlare(blockTime *big.Int, to common.Address) bool { + return blockTime.Cmp(flareActivationTime) >= 0 && + to == common.HexToAddress("0x1000000000000000000000000000000000000001") +} + +func GetStateConnectorIsActivatedAndCalledCostwo(blockTime *big.Int, to common.Address) bool { + return blockTime.Cmp(costwoActivationTime) >= 0 && + to == common.HexToAddress("0x1000000000000000000000000000000000000001") +} + +func GetStateConnectorIsActivatedAndCalledStaging(blockTime *big.Int, to common.Address) bool { + return blockTime.Cmp(stagingActivationTime) >= 0 && + to == common.HexToAddress("0x1000000000000000000000000000000000000001") +} + +func GetStateConnectorIsActivatedAndCalledLocalFlare(blockTime *big.Int, to common.Address) bool { + return blockTime.Cmp(localFlareActivationTime) >= 0 && + to == common.HexToAddress("0x1000000000000000000000000000000000000001") +} + +func GetStateConnectorIsActivatedAndCalledLocal(_ *big.Int, to common.Address) bool { + return to == common.HexToAddress("0x1000000000000000000000000000000000000001") +} + +func GetStateConnectorIsActivatedAndCalledSongbird(blockTime *big.Int, to common.Address) bool { switch { - case chainID.Cmp(params.FlareChainID) == 0 && blockTime.Cmp(flareActivationTime) >= 0: - switch blockTime { - default: - return to == common.HexToAddress("0x1000000000000000000000000000000000000001") - } - case chainID.Cmp(params.SongbirdChainID) == 0 && blockTime.Cmp(songbirdActivationTime) >= 0: - switch blockTime { - default: - return to == common.HexToAddress("0x3A1b3220527aBA427d1e13e4b4c48c31460B4d91") - } - case chainID.Cmp(params.CostwoChainID) == 0 && blockTime.Cmp(costwoActivationTime) >= 0: - switch blockTime { - default: - return to == common.HexToAddress("0x1000000000000000000000000000000000000001") - } - case chainID.Cmp(params.StagingChainID) == 0 && blockTime.Cmp(stagingActivationTime) >= 0: - switch blockTime { - default: - return to == common.HexToAddress("0x1000000000000000000000000000000000000001") - } - case chainID.Cmp(params.LocalFlareChainID) == 0 && blockTime.Cmp(localFlareActivationTime) >= 0: - switch blockTime { - default: - return to == common.HexToAddress("0x1000000000000000000000000000000000000001") - } + case blockTime.Cmp(songbirdOct22ForkTime) > 0: + return to == common.HexToAddress("0x0c13aDA1C7143Cf0a0795FFaB93eEBb6FAD6e4e3") + case blockTime.Cmp(songbirdActivationTime) > 0: + return to == common.HexToAddress("0x3A1b3220527aBA427d1e13e4b4c48c31460B4d91") + default: + return false + } +} + +func GetStateConnectorIsActivatedAndCalledCoston(blockTime *big.Int, to common.Address) bool { + switch { + case blockTime.Cmp(costonOct22ForkTime) > 0: + return to == common.HexToAddress("0x0c13aDA1C7143Cf0a0795FFaB93eEBb6FAD6e4e3") + case blockTime.Cmp(costonActivationTime) > 0: + return to == common.HexToAddress("0x947c76694491d3fD67a73688003c4d36C8780A97") default: return false } diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index 77d815d3..dd64582b 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -362,16 +362,19 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) } var ( - ret []byte - vmerr error // vm errors do not affect consensus and are therefore not assigned to err - chainID *big.Int - timestamp *big.Int - burnAddress common.Address + ret []byte + vmerr error // vm errors do not affect consensus and are therefore not assigned to err + chainID *big.Int + timestamp *big.Int ) chainID = st.evm.ChainConfig().ChainID timestamp = st.evm.Context.Time - burnAddress = common.HexToAddress("0x000000000000000000000000000000000000dEaD") + + burnAddress, nominalGasPrice, isSongbird, isFlare, err := stateTransitionVariants.GetValue(chainID)(st) + if err != nil { + return nil, err + } if contractCreation { ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value) @@ -379,49 +382,19 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { // Increment the nonce for the next transaction st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) - if vmerr == nil && chainID != nil && timestamp != nil && st.evm.Context.Coinbase == common.HexToAddress("0x0100000000000000000000000000000000000000") { - if GetStateConnectorIsActivatedAndCalled(chainID, timestamp, *msg.To()) && - len(st.data) >= 36 && len(ret) == 32 && - bytes.Equal(st.data[0:4], SubmitAttestationSelector(chainID, timestamp)) && - binary.BigEndian.Uint64(ret[24:32]) > 0 { - err = st.FinalisePreviousRound(chainID, timestamp, st.data[4:36]) - if err != nil { - log.Warn("Error finalising state connector round", "error", err) - } - } else if GetGovernanceSettingIsActivatedAndCalled(chainID, timestamp, *msg.To()) && len(st.data) == 36 { - if bytes.Equal(st.data[0:4], SetGovernanceAddressSelector(chainID, timestamp)) { - err = st.SetGovernanceAddress(chainID, timestamp, st.data[4:36]) - if err != nil { - log.Warn("Error setting governance address", "error", err) - } - } else if bytes.Equal(st.data[0:4], SetTimelockSelector(chainID, timestamp)) { - err = st.SetTimelock(chainID, timestamp, st.data[4:36]) - if err != nil { - log.Warn("Error setting governance timelock", "error", err) - } - } - } else if GetInitialAirdropChangeIsActivatedAndCalled(chainID, timestamp, *msg.To()) && len(st.data) == 4 { - if bytes.Equal(st.data[0:4], UpdateInitialAirdropAddressSelector(chainID, timestamp)) { - err = st.UpdateInitialAirdropAddress(chainID, timestamp) - if err != nil { - log.Warn("Error updating initialAirdrop contract", "error", err) - } - } - } else if GetDistributionChangeIsActivatedAndCalled(chainID, timestamp, *msg.To()) && len(st.data) == 4 { - if bytes.Equal(st.data[0:4], UpdateDistributionAddressSelector(chainID, timestamp)) { - err = st.UpdateDistributionAddress(chainID, timestamp) - if err != nil { - log.Warn("Error updating distribution contract", "error", err) - } - } + if vmerr == nil && chainID != nil && timestamp != nil { + if isSongbird { + handleSongbirdTransitionDbContracts(st, chainID, timestamp, msg, ret) + } else { + handleFlareTransitionDbContracts(st, chainID, timestamp, msg, ret) } } } st.refundGas(rules.IsApricotPhase1) - if vmerr == nil && IsPrioritisedContractCall(chainID, msg.To(), ret, timestamp) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { - nominalGasUsed := params.TxGas // 21000 - nominalGasPrice := uint64(params.ApricotPhase4MinBaseFee) // 25_000_000_000; the max base fee is 1_000_000_000_000 + + if vmerr == nil && IsPrioritisedContractCall(chainID, timestamp, msg.To(), ret, st.initialGas) { + nominalGasUsed := params.TxGas // 21000 nominalFee := new(big.Int).Mul(new(big.Int).SetUint64(nominalGasUsed), new(big.Int).SetUint64(nominalGasPrice)) actualGasUsed := st.gasUsed() actualGasPrice := st.gasPrice @@ -438,7 +411,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } // Call the daemon if there is no vm error - if vmerr == nil && chainID != nil && (chainID.Cmp(params.FlareChainID) == 0 || chainID.Cmp(params.SongbirdChainID) == 0 || chainID.Cmp(params.CostwoChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0 || chainID.Cmp(params.LocalFlareChainID) == 0) { + if vmerr == nil && (isSongbird || isFlare) { log := log.Root() atomicDaemonAndMint(st, log) } @@ -450,6 +423,54 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { }, nil } +func handleSongbirdTransitionDbContracts(st *StateTransition, chainID *big.Int, timestamp *big.Int, msg Message, ret []byte) { + if GetStateConnectorIsActivatedAndCalled(chainID, timestamp, *msg.To()) && + len(st.data) >= 36 && len(ret) == 32 && + bytes.Equal(st.data[0:4], SubmitAttestationSelector(chainID, timestamp)) && + binary.BigEndian.Uint64(ret[24:32]) > 0 { + if err := st.FinalisePreviousRound(chainID, timestamp, st.data[4:36]); err != nil { + log.Warn("Error finalising state connector round", "error", err) + } + } +} + +func handleFlareTransitionDbContracts(st *StateTransition, chainID *big.Int, timestamp *big.Int, msg Message, ret []byte) { + if st.evm.Context.Coinbase != common.HexToAddress("0x0100000000000000000000000000000000000000") { + return + } + + if GetStateConnectorIsActivatedAndCalled(chainID, timestamp, *msg.To()) && + len(st.data) >= 36 && len(ret) == 32 && + bytes.Equal(st.data[0:4], SubmitAttestationSelector(chainID, timestamp)) && + binary.BigEndian.Uint64(ret[24:32]) > 0 { + if err := st.FinalisePreviousRound(chainID, timestamp, st.data[4:36]); err != nil { + log.Warn("Error finalising state connector round", "error", err) + } + } else if GetGovernanceSettingIsActivatedAndCalled(chainID, timestamp, *msg.To()) && len(st.data) == 36 { + if bytes.Equal(st.data[0:4], SetGovernanceAddressSelector(chainID, timestamp)) { + if err := st.SetGovernanceAddress(chainID, timestamp, st.data[4:36]); err != nil { + log.Warn("Error setting governance address", "error", err) + } + } else if bytes.Equal(st.data[0:4], SetTimelockSelector(chainID, timestamp)) { + if err := st.SetTimelock(chainID, timestamp, st.data[4:36]); err != nil { + log.Warn("Error setting governance timelock", "error", err) + } + } + } else if GetInitialAirdropChangeIsActivatedAndCalled(chainID, timestamp, *msg.To()) && len(st.data) == 4 { + if bytes.Equal(st.data[0:4], UpdateInitialAirdropAddressSelector(chainID, timestamp)) { + if err := st.UpdateInitialAirdropAddress(chainID, timestamp); err != nil { + log.Warn("Error updating initialAirdrop contract", "error", err) + } + } + } else if GetDistributionChangeIsActivatedAndCalled(chainID, timestamp, *msg.To()) && len(st.data) == 4 { + if bytes.Equal(st.data[0:4], UpdateDistributionAddressSelector(chainID, timestamp)) { + if err := st.UpdateDistributionAddress(chainID, timestamp); err != nil { + log.Warn("Error updating distribution contract", "error", err) + } + } + } +} + func (st *StateTransition) refundGas(apricotPhase1 bool) { // Inspired by: https://gist.github.com/holiman/460f952716a74eeb9ab358bb1836d821#gistcomment-3642048 if !apricotPhase1 { diff --git a/coreth/core/state_transition_params.go b/coreth/core/state_transition_params.go new file mode 100644 index 00000000..ebd8315b --- /dev/null +++ b/coreth/core/state_transition_params.go @@ -0,0 +1,38 @@ +package core + +import ( + "errors" + "math/big" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/utils" + "github.com/ethereum/go-ethereum/common" +) + +var ( + stateTransitionVariants = utils.NewChainValue(invalidChain). + AddValues([]*big.Int{params.FlareChainID, params.CostwoChainID, params.StagingChainID, params.LocalFlareChainID}, stateTransitionParamsFlare). + AddValues([]*big.Int{params.SongbirdChainID, params.CostonChainID, params.LocalChainID}, stateTransitionParamsSongbird) +) + +func invalidChain(st *StateTransition) (common.Address, uint64, bool, bool, error) { + return common.Address{}, 0, false, false, errors.New("invalid chain ID") +} + +// Returns the state transition parameters for the given chain ID +// burnAddress, nominalGasPrice, isFlare chain, isSongbird chain, error +func stateTransitionParamsFlare(st *StateTransition) (common.Address, uint64, bool, bool, error) { + return common.HexToAddress("0x000000000000000000000000000000000000dEaD"), + uint64(params.ApricotPhase4MinBaseFee), + true, + false, + nil +} + +func stateTransitionParamsSongbird(st *StateTransition) (common.Address, uint64, bool, bool, error) { + burnAddress := st.evm.Context.Coinbase + if burnAddress != common.HexToAddress("0x0100000000000000000000000000000000000000") { + return common.Address{}, 0, false, true, errors.New("invalid value for block.coinbase") + } + return burnAddress, 225_000_000_000, false, true, nil +} diff --git a/coreth/utils/flare_choice.go b/coreth/utils/flare_choice.go new file mode 100644 index 00000000..7279d065 --- /dev/null +++ b/coreth/utils/flare_choice.go @@ -0,0 +1,34 @@ +package utils + +import "math/big" + +type ChainValue[T any] struct { + valueMap map[uint64]T + defaultValue T +} + +func NewChainValue[T any](defaultValue T) *ChainValue[T] { + return &ChainValue[T]{ + valueMap: make(map[uint64]T), + defaultValue: defaultValue, + } +} + +func (ca *ChainValue[T]) AddValue(chainID *big.Int, action T) *ChainValue[T] { + ca.valueMap[chainID.Uint64()] = action + return ca +} + +func (ca *ChainValue[T]) AddValues(chainIDs []*big.Int, action T) *ChainValue[T] { + for _, chainID := range chainIDs { + ca.valueMap[chainID.Uint64()] = action + } + return ca +} + +func (ca *ChainValue[T]) GetValue(chainID *big.Int) T { + if action, ok := ca.valueMap[chainID.Uint64()]; ok { + return action + } + return ca.defaultValue +} From 0853dca722e41b9d35b2cb047ea5432e50582872 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 20 Mar 2024 22:11:08 +0100 Subject: [PATCH 12/39] Refactored inflation settings --- avalanchego/utils/network_value.go | 32 +++ .../txs/executor/inflation_settings.go | 241 ++++++++++-------- 2 files changed, 170 insertions(+), 103 deletions(-) create mode 100644 avalanchego/utils/network_value.go diff --git a/avalanchego/utils/network_value.go b/avalanchego/utils/network_value.go new file mode 100644 index 00000000..288d55ba --- /dev/null +++ b/avalanchego/utils/network_value.go @@ -0,0 +1,32 @@ +package utils + +type NetworkValue[T any] struct { + valueMap map[uint32]T + defaultValue T +} + +func NewNetworkValue[T any](defaultValue T) *NetworkValue[T] { + return &NetworkValue[T]{ + valueMap: make(map[uint32]T), + defaultValue: defaultValue, + } +} + +func (ca *NetworkValue[T]) AddValue(networkID uint32, action T) *NetworkValue[T] { + ca.valueMap[networkID] = action + return ca +} + +func (ca *NetworkValue[T]) AddValues(networkIDs []uint32, action T) *NetworkValue[T] { + for _, networkID := range networkIDs { + ca.valueMap[networkID] = action + } + return ca +} + +func (ca *NetworkValue[T]) GetValue(networkID uint32) T { + if action, ok := ca.valueMap[networkID]; ok { + return action + } + return ca.defaultValue +} diff --git a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go index 37216686..772a7d99 100644 --- a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go +++ b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go @@ -3,128 +3,163 @@ package executor import ( "time" + "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/platformvm/config" ) +var inflationSettingsVariants = utils.NewNetworkValue(getDefaultInflationSettings). + AddValue(constants.FlareID, getFlareInflationSettings). + AddValue(constants.CostwoID, getCostwoInflationSettings). + AddValue(constants.LocalFlareID, getLocalFlareInflationSettings). + AddValue(constants.StagingID, getStagingInflationSettings) + +type InflationSettings struct { + MinValidatorStake uint64 + MaxValidatorStake uint64 + MinDelegatorStake uint64 + MinDelegationFee uint32 + MinStakeDuration time.Duration + MinDelegateDuration time.Duration + MaxStakeDuration time.Duration + MinFutureStartTimeOffset time.Duration + MaxValidatorWeightFactor uint64 + MinStakeStartTime time.Time +} + // The value of currentTimestamp is used to return new inflation settings over time func GetCurrentInflationSettings(currentTimestamp time.Time, networkID uint32, config *config.Config) (uint64, uint64, uint64, uint32, time.Duration, time.Duration, time.Duration, time.Duration, uint64, time.Time) { + s := inflationSettingsVariants.GetValue(networkID)(currentTimestamp, config) + return s.MinValidatorStake, s.MaxValidatorStake, s.MinDelegatorStake, s.MinDelegationFee, s.MinStakeDuration, s.MinDelegateDuration, s.MaxStakeDuration, s.MinFutureStartTimeOffset, s.MaxValidatorWeightFactor, s.MinStakeStartTime +} + +func getFlareInflationSettings(currentTimestamp time.Time, _ *config.Config) InflationSettings { switch { - case currentTimestamp.Before(getPhaseTwoStakingStartTime(networkID)): - return getPhaseOneInflationSettings(networkID, config) + case currentTimestamp.Before(time.Date(2023, time.October, 1, 0, 0, 0, 0, time.UTC)): + // Phase 1 + return InflationSettings{ + MinValidatorStake: 10 * units.MegaAvax, + MaxValidatorStake: 50 * units.MegaAvax, + MinDelegatorStake: 1 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 2 * 7 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: 3 * 24 * time.Hour, + MaxValidatorWeightFactor: MaxValidatorWeightFactor, + MinStakeStartTime: time.Date(2023, time.July, 5, 15, 0, 0, 0, time.UTC), + } default: - return getPhaseTwoInflationSettings(networkID, config) + // Phase 2 + return InflationSettings{ + MinValidatorStake: 1 * units.MegaAvax, + MaxValidatorStake: 200 * units.MegaAvax, + MinDelegatorStake: 50 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 60 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: 15, + MinStakeStartTime: time.Date(2023, time.October, 1, 0, 0, 0, 0, time.UTC), + } } } -func getPhaseTwoStakingStartTime(networkID uint32) time.Time { - switch networkID { - case constants.FlareID: - return time.Date(2023, time.October, 1, 0, 0, 0, 0, time.UTC) - case constants.CostwoID: - return time.Date(2023, time.September, 7, 0, 0, 0, 0, time.UTC) +func getCostwoInflationSettings(currentTimestamp time.Time, _ *config.Config) InflationSettings { + switch { + case currentTimestamp.Before(time.Date(2023, time.September, 7, 0, 0, 0, 0, time.UTC)): + // Phase 1 + return InflationSettings{ + MinValidatorStake: 100 * units.KiloAvax, + MaxValidatorStake: 50 * units.MegaAvax, + MinDelegatorStake: 1 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 2 * 7 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: MaxValidatorWeightFactor, + MinStakeStartTime: time.Date(2023, time.May, 25, 15, 0, 0, 0, time.UTC), + } default: - return time.Date(2023, time.August, 1, 0, 0, 0, 0, time.UTC) + // Phase 2 + return InflationSettings{ + MinValidatorStake: 1 * units.MegaAvax, + MaxValidatorStake: 200 * units.MegaAvax, + MinDelegatorStake: 50 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 60 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: 15, + MinStakeStartTime: time.Date(2023, time.September, 7, 0, 0, 0, 0, time.UTC), + } } } -func getPhaseOneInflationSettings(networkID uint32, config *config.Config) (uint64, uint64, uint64, uint32, time.Duration, time.Duration, time.Duration, time.Duration, uint64, time.Time) { - switch networkID { - case constants.FlareID: - return 10 * units.MegaAvax, // minValidatorStake - 50 * units.MegaAvax, // maxValidatorStake - 1 * units.KiloAvax, // minDelegatorStake - 0, // minDelegationFee - 2 * 7 * 24 * time.Hour, // minStakeDuration - 2 * 7 * 24 * time.Hour, // minDelegateDuration - 365 * 24 * time.Hour, // maxStakeDuration - 3 * 24 * time.Hour, // minFutureStartTimeOffset - MaxValidatorWeightFactor, // maxValidatorWeightFactor - time.Date(2023, time.July, 5, 15, 0, 0, 0, time.UTC) // minStakeStartTime - case constants.CostwoID: - return 100 * units.KiloAvax, - 50 * units.MegaAvax, - 1 * units.KiloAvax, - 0, - 2 * 7 * 24 * time.Hour, - 2 * 7 * 24 * time.Hour, - 365 * 24 * time.Hour, - MaxFutureStartTime, - MaxValidatorWeightFactor, - time.Date(2023, time.May, 25, 15, 0, 0, 0, time.UTC) - case constants.StagingID: - return 100 * units.KiloAvax, - 50 * units.MegaAvax, - 1 * units.KiloAvax, - 0, - 2 * 7 * 24 * time.Hour, - 2 * 7 * 24 * time.Hour, - 365 * 24 * time.Hour, - MaxFutureStartTime, - MaxValidatorWeightFactor, - time.Date(2023, time.May, 10, 15, 0, 0, 0, time.UTC) - case constants.LocalFlareID: - return 10 * units.KiloAvax, - 50 * units.MegaAvax, - 10 * units.KiloAvax, - 0, - 2 * 7 * 24 * time.Hour, - 2 * 7 * 24 * time.Hour, - 365 * 24 * time.Hour, - 24 * time.Hour, - MaxValidatorWeightFactor, - time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC) +func getLocalFlareInflationSettings(currentTimestamp time.Time, _ *config.Config) InflationSettings { + switch { + case currentTimestamp.Before(time.Date(2023, time.August, 1, 0, 0, 0, 0, time.UTC)): + // Phase 1 + return InflationSettings{ + MinValidatorStake: 10 * units.KiloAvax, + MaxValidatorStake: 50 * units.MegaAvax, + MinDelegatorStake: 10 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 2 * 7 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: 24 * time.Hour, + MaxValidatorWeightFactor: MaxValidatorWeightFactor, + MinStakeStartTime: time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC), + } default: - return config.MinValidatorStake, - config.MaxValidatorStake, - config.MinDelegatorStake, - config.MinDelegationFee, - config.MinStakeDuration, - config.MinStakeDuration, - config.MaxStakeDuration, - MaxFutureStartTime, - MaxValidatorWeightFactor, - time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + // Phase 2 + return InflationSettings{ + MinValidatorStake: 10 * units.KiloAvax, + MaxValidatorStake: 50 * units.MegaAvax, + MinDelegatorStake: 10 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 2 * 7 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: MaxValidatorWeightFactor, + MinStakeStartTime: time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC), + } } } -func getPhaseTwoInflationSettings(networkID uint32, config *config.Config) (uint64, uint64, uint64, uint32, time.Duration, time.Duration, time.Duration, time.Duration, uint64, time.Time) { - switch networkID { - case constants.FlareID: - return 1 * units.MegaAvax, // minValidatorStake - 200 * units.MegaAvax, // maxValidatorStake - 50 * units.KiloAvax, // minDelegatorStake - 0, // minDelegationFee - 60 * 24 * time.Hour, // minStakeDuration - 2 * 7 * 24 * time.Hour, // minDelegateDuration - 365 * 24 * time.Hour, // maxStakeDuration - MaxFutureStartTime, // minFutureStartTimeOffset - 15, // maxValidatorWeightFactor - time.Date(2023, time.October, 1, 0, 0, 0, 0, time.UTC) // minStakeStartTime - case constants.CostwoID: - return 1 * units.MegaAvax, - 200 * units.MegaAvax, - 50 * units.KiloAvax, - 0, - 60 * 24 * time.Hour, - 2 * 7 * 24 * time.Hour, - 365 * 24 * time.Hour, - MaxFutureStartTime, - 15, - time.Date(2023, time.September, 7, 0, 0, 0, 0, time.UTC) - case constants.LocalFlareID: - return 10 * units.KiloAvax, - 50 * units.MegaAvax, - 10 * units.KiloAvax, - 0, - 2 * 7 * 24 * time.Hour, - 2 * 7 * 24 * time.Hour, - 365 * 24 * time.Hour, - MaxFutureStartTime, - MaxValidatorWeightFactor, - time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC) - default: - return getPhaseOneInflationSettings(networkID, config) +func getStagingInflationSettings(_ time.Time, _ *config.Config) InflationSettings { + // Phase 1 + return InflationSettings{ + MinValidatorStake: 100 * units.KiloAvax, + MaxValidatorStake: 50 * units.MegaAvax, + MinDelegatorStake: 1 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 2 * 7 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: MaxValidatorWeightFactor, + MinStakeStartTime: time.Date(2023, time.May, 10, 15, 0, 0, 0, time.UTC), + } +} + +func getDefaultInflationSettings(_ time.Time, config *config.Config) InflationSettings { + return InflationSettings{ + MinValidatorStake: config.MinValidatorStake, + MaxValidatorStake: config.MaxValidatorStake, + MinDelegatorStake: config.MinDelegatorStake, + MinDelegationFee: config.MinDelegationFee, + MinStakeDuration: config.MinStakeDuration, + MinDelegateDuration: config.MinStakeDuration, + MaxStakeDuration: config.MaxStakeDuration, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: MaxValidatorWeightFactor, + MinStakeStartTime: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), } } From 926a5a6667097301e2fc95cfda5f8cdc0e6f7420 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 27 Mar 2024 14:29:55 +0100 Subject: [PATCH 13/39] Changed next time when no validators --- .../txs/executor/inflation_settings.go | 25 ++++++++++++++++++- .../txs/executor/proposal_tx_executor.go | 6 ++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go index 772a7d99..dbfc8e4c 100644 --- a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go +++ b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go @@ -13,7 +13,8 @@ var inflationSettingsVariants = utils.NewNetworkValue(getDefaultInflationSetting AddValue(constants.FlareID, getFlareInflationSettings). AddValue(constants.CostwoID, getCostwoInflationSettings). AddValue(constants.LocalFlareID, getLocalFlareInflationSettings). - AddValue(constants.StagingID, getStagingInflationSettings) + AddValue(constants.StagingID, getStagingInflationSettings). + AddValue(constants.LocalID, getLocalInflationSettings) type InflationSettings struct { MinValidatorStake uint64 @@ -149,6 +150,28 @@ func getStagingInflationSettings(_ time.Time, _ *config.Config) InflationSetting } } +func getLocalInflationSettings(currentTimestamp time.Time, config *config.Config) InflationSettings { + switch { + case currentTimestamp.Before(time.Date(2000, time.March, 1, 0, 0, 0, 0, time.UTC)): + // Phase 1 + return getDefaultInflationSettings(currentTimestamp, config) + default: + // Phase 2 + return InflationSettings{ + MinValidatorStake: 10 * units.KiloAvax, + MaxValidatorStake: 50 * units.MegaAvax, + MinDelegatorStake: 10 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 2 * time.Hour, + MinDelegateDuration: 20 * time.Minute, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: MaxValidatorWeightFactor, + MinStakeStartTime: time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC), + } + } +} + func getDefaultInflationSettings(_ time.Time, config *config.Config) InflationSettings { return InflationSettings{ MinValidatorStake: config.MinValidatorStake, diff --git a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go index d93a0953..47c27ec6 100644 --- a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go +++ b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go @@ -49,6 +49,10 @@ var ( errEmptyNodeID = errors.New("validator nodeID cannot be empty") ) +var ( + songbirdLatestStakingTime = time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC) +) + type ProposalTxExecutor struct { // inputs, to be filled before visitor methods are called *Backend @@ -866,7 +870,7 @@ func GetNextStakerChangeTime(state state.Chain) (time.Time, error) { default: // SGB-MERGE if state.GetNetworkID() == constants.SongbirdID || state.GetNetworkID() == constants.CostonID || state.GetNetworkID() == constants.LocalID { - return state.GetTimestamp().Add(time.Second), nil + return songbirdLatestStakingTime, nil } return time.Time{}, database.ErrNotFound } From dd7b6105738196ef887b8c68fe3dd2dc3bcc5199 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Fri, 29 Mar 2024 14:38:12 +0100 Subject: [PATCH 14/39] Added songbird specific target gas --- coreth/consensus/dummy/consensus.go | 4 ++-- coreth/consensus/dummy/dynamic_fees.go | 6 +++++- coreth/core/chain_makers.go | 2 +- coreth/miner/worker.go | 2 +- coreth/params/avalanche_params.go | 3 ++- coreth/plugin/evm/block_verification.go | 10 ++-------- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/coreth/consensus/dummy/consensus.go b/coreth/consensus/dummy/consensus.go index 80aab076..dd48bf94 100644 --- a/coreth/consensus/dummy/consensus.go +++ b/coreth/consensus/dummy/consensus.go @@ -99,8 +99,8 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade if config.IsSongbirdCode() { // SGB-MERGE // Verify that the gas limit is correct for the current phase - if header.GasLimit != params.ApricotPhase5GasLimit { - return fmt.Errorf("expected gas limit to be %d in apricot phase 5 but got %d", params.ApricotPhase5GasLimit, header.GasLimit) + if header.GasLimit != params.SgbApricotPhase5GasLimit { + return fmt.Errorf("expected gas limit to be %d in apricot phase 5 but got %d", params.SgbApricotPhase5GasLimit, header.GasLimit) } } else { if config.IsApricotPhase1(timestamp) { diff --git a/coreth/consensus/dummy/dynamic_fees.go b/coreth/consensus/dummy/dynamic_fees.go index aa38fcce..527a46f3 100644 --- a/coreth/consensus/dummy/dynamic_fees.go +++ b/coreth/consensus/dummy/dynamic_fees.go @@ -76,7 +76,11 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uin ) if isApricotPhase5 { baseFeeChangeDenominator = ApricotPhase5BaseFeeChangeDenominator - parentGasTarget = params.ApricotPhase5TargetGas + if config.IsSongbirdCode() { + parentGasTarget = params.SgbApricotPhase5TargetGas + } else { + parentGasTarget = params.ApricotPhase5TargetGas + } } parentGasTargetBig := new(big.Int).SetUint64(parentGasTarget) diff --git a/coreth/core/chain_makers.go b/coreth/core/chain_makers.go index 819d6f8e..9fb78557 100644 --- a/coreth/core/chain_makers.go +++ b/coreth/core/chain_makers.go @@ -292,7 +292,7 @@ func makeHeader(chain consensus.ChainReader, config *params.ChainConfig, parent var gasLimit uint64 if config.IsSongbirdCode() { // SGB-MERGE - gasLimit = params.ApricotPhase5GasLimit + gasLimit = params.SgbApricotPhase5GasLimit } else { if config.IsApricotPhase1(timestamp) { gasLimit = params.ApricotPhase1GasLimit diff --git a/coreth/miner/worker.go b/coreth/miner/worker.go index 0e6f5299..d72a185d 100644 --- a/coreth/miner/worker.go +++ b/coreth/miner/worker.go @@ -129,7 +129,7 @@ func (w *worker) commitNewWork() (*types.Block, error) { var gasLimit uint64 if w.chainConfig.IsSongbirdCode() { - gasLimit = params.ApricotPhase5GasLimit + gasLimit = params.SgbApricotPhase5GasLimit } else { if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { gasLimit = params.ApricotPhase1GasLimit diff --git a/coreth/params/avalanche_params.go b/coreth/params/avalanche_params.go index eb47f134..d7b32aea 100644 --- a/coreth/params/avalanche_params.go +++ b/coreth/params/avalanche_params.go @@ -21,7 +21,7 @@ const ( ApricotPhase1GasLimit uint64 = 8_000_000 // SGB-MERGE - ApricotPhase5GasLimit uint64 = 30_000_000 + SgbApricotPhase5GasLimit uint64 = 30_000_000 ApricotPhase3ExtraDataSize uint64 = 80 ApricotPhase3MinBaseFee int64 = 75_000_000_000 @@ -32,6 +32,7 @@ const ( ApricotPhase4MaxBaseFee int64 = 1_000_000_000_000 ApricotPhase4BaseFeeChangeDenominator uint64 = 12 ApricotPhase5TargetGas uint64 = 15_000_000 + SgbApricotPhase5TargetGas uint64 = 150_000_000 ApricotPhase5BaseFeeChangeDenominator uint64 = 36 // The base cost to charge per atomic transaction. Added in Apricot Phase 5. diff --git a/coreth/plugin/evm/block_verification.go b/coreth/plugin/evm/block_verification.go index 0a2ad942..35efa39c 100644 --- a/coreth/plugin/evm/block_verification.go +++ b/coreth/plugin/evm/block_verification.go @@ -8,7 +8,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" safemath "github.com/ava-labs/avalanchego/utils/math" @@ -100,17 +99,12 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { // Enforce static gas limit after ApricotPhase1 (prior to ApricotPhase1 it's handled in processing). - // DEBUG !!!!! - log.Info(fmt.Sprintf("DEBUG: ethHeader.GasLimit: %d", ethHeader.GasLimit)) - log.Info(fmt.Sprintf("DEBUG IsSongbirdCode: %t", rules.IsSongbirdCode)) - log.Info(fmt.Sprintf("DEBUG chain id: %v", rules.ChainID.Int64())) - if rules.IsSongbirdCode { // SGB-MERGE - if ethHeader.GasLimit != params.ApricotPhase5GasLimit { + if ethHeader.GasLimit != params.SgbApricotPhase5GasLimit { return fmt.Errorf( "expected gas limit to be %d in apricot phase 5 but got %d", - params.ApricotPhase5GasLimit, ethHeader.GasLimit, + params.SgbApricotPhase5GasLimit, ethHeader.GasLimit, ) } } else { From 15b203db83e455c83f1a1499ab718b6fa187f0ad Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 1 Apr 2024 21:05:15 +0200 Subject: [PATCH 15/39] changed chain name for songbird networks, changes to params after testing bootstraping --- avalanchego/genesis/config.go | 2 + avalanchego/main/main.go | 3 ++ avalanchego/version/application.go | 4 +- avalanchego/version/constants.go | 59 +++++++++++++++++++++----- avalanchego/version/parser.go | 6 ++- avalanchego/version/parser_test.go | 3 ++ coreth/consensus/dummy/consensus.go | 24 +++++++++-- coreth/consensus/dummy/dynamic_fees.go | 24 +++++++---- 8 files changed, 100 insertions(+), 25 deletions(-) diff --git a/avalanchego/genesis/config.go b/avalanchego/genesis/config.go index fda76ae1..0af45243 100644 --- a/avalanchego/genesis/config.go +++ b/avalanchego/genesis/config.go @@ -242,10 +242,12 @@ func init() { LocalFlareConfig = localFlareConfig songbirdConfig, err := unparsedSongbirdConfig.Parse() + songbirdConfig.CChainGenesis = songbirdCChainGenesis errs.Add(err) SongbirdConfig = songbirdConfig costonConfig, err := unparsedCostonConfig.Parse() + costonConfig.CChainGenesis = costonCChainGenesis errs.Add(err) CostonConfig = costonConfig diff --git a/avalanchego/main/main.go b/avalanchego/main/main.go index a67a0648..aa93d291 100644 --- a/avalanchego/main/main.go +++ b/avalanchego/main/main.go @@ -46,5 +46,8 @@ func main() { os.Exit(1) } + // Flare specific: set the application prefix (flare for songbird and avalanche for flare) + version.InitApplicationPrefix(nodeConfig.NetworkID) + runner.Run(runnerConfig, nodeConfig) } diff --git a/avalanchego/version/application.go b/avalanchego/version/application.go index 2b799417..3ce5d106 100644 --- a/avalanchego/version/application.go +++ b/avalanchego/version/application.go @@ -24,7 +24,7 @@ type Application struct { } // The only difference here between Application and Semantic is that Application -// prepends "avalanche/" rather than "v". +// prepends "avalanche/" or "flare/" rather than "v". func (a *Application) String() string { strIntf := a.str.Load() if strIntf != nil { @@ -32,7 +32,7 @@ func (a *Application) String() string { } str := fmt.Sprintf( - "avalanche/%d.%d.%d", + GetApplicationPrefix()+"/%d.%d.%d", a.Major, a.Minor, a.Patch, diff --git a/avalanchego/version/constants.go b/avalanchego/version/constants.go index 57654a75..ca911101 100644 --- a/avalanchego/version/constants.go +++ b/avalanchego/version/constants.go @@ -32,6 +32,27 @@ var ( Patch: 0, } + CurrentSgb = &Semantic{ + Major: 0, + Minor: 6, + Patch: 6, + } + CurrentSgbApp = &Application{ + Major: CurrentSgb.Major, + Minor: CurrentSgb.Minor, + Patch: CurrentSgb.Patch, + } + MinimumCompatibleSgbVersion = &Application{ + Major: 0, + Minor: 6, + Patch: 5, + } + PrevMinimumCompatibleSgbVersion = &Application{ + Major: 0, + Minor: 6, + Patch: 4, + } + CurrentDatabase = DatabaseVersion1_4_5 PrevDatabase = DatabaseVersion1_0_0 @@ -47,22 +68,26 @@ var ( } ApricotPhase3Times = map[uint32]time.Time{ - constants.MainnetID: time.Date(2021, time.August, 24, 14, 0, 0, 0, time.UTC), - constants.FujiID: time.Date(2021, time.August, 16, 19, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(2021, time.August, 24, 14, 0, 0, 0, time.UTC), + // constants.FujiID: time.Date(2021, time.August, 16, 19, 0, 0, 0, time.UTC), constants.FlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.CostonID: time.Date(2022, time.February, 25, 14, 0, 0, 0, time.UTC), + constants.SongbirdID: time.Date(2022, time.March, 7, 14, 0, 0, 0, time.UTC), } ApricotPhase3DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) ApricotPhase4Times = map[uint32]time.Time{ - constants.MainnetID: time.Date(2021, time.September, 22, 21, 0, 0, 0, time.UTC), - constants.FujiID: time.Date(2021, time.September, 16, 21, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(2021, time.September, 22, 21, 0, 0, 0, time.UTC), + // constants.FujiID: time.Date(2021, time.September, 16, 21, 0, 0, 0, time.UTC), constants.FlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.CostonID: time.Date(2022, time.February, 25, 15, 0, 0, 0, time.UTC), + constants.SongbirdID: time.Date(2022, time.March, 7, 15, 0, 0, 0, time.UTC), } ApricotPhase4DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) ApricotPhase4MinPChainHeight = map[uint32]uint64{ @@ -72,34 +97,40 @@ var ( ApricotPhase4DefaultMinPChainHeight uint64 ApricotPhase5Times = map[uint32]time.Time{ - constants.MainnetID: time.Date(2021, time.December, 2, 18, 0, 0, 0, time.UTC), - constants.FujiID: time.Date(2021, time.November, 24, 15, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(2021, time.December, 2, 18, 0, 0, 0, time.UTC), + // constants.FujiID: time.Date(2021, time.November, 24, 15, 0, 0, 0, time.UTC), constants.FlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.CostonID: time.Date(2022, time.February, 25, 16, 0, 0, 0, time.UTC), + constants.SongbirdID: time.Date(2022, time.March, 7, 16, 0, 0, 0, time.UTC), } ApricotPhase5DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) // FIXME: update this before release BlueberryTimes = map[uint32]time.Time{ - constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), - constants.FujiID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + // constants.FujiID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.FlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.CostonID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.SongbirdID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), } BlueberryDefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) // FIXME: update this before release XChainMigrationTimes = map[uint32]time.Time{ - constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), - constants.FujiID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + // constants.FujiID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.FlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.CostonID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.SongbirdID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), } XChainMigrationDefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) ) @@ -147,6 +178,14 @@ func GetXChainMigrationTime(networkID uint32) time.Time { } func GetCompatibility(networkID uint32) Compatibility { + if networkID == constants.SongbirdID || networkID == constants.CostonID || networkID == constants.LocalID { + return NewCompatibility( + CurrentSgbApp, + MinimumCompatibleSgbVersion, + GetApricotPhase5Time(networkID), + PrevMinimumCompatibleSgbVersion, + ) + } return NewCompatibility( CurrentApp, MinimumCompatibleVersion, diff --git a/avalanchego/version/parser.go b/avalanchego/version/parser.go index ebfdda2c..27c2eb96 100644 --- a/avalanchego/version/parser.go +++ b/avalanchego/version/parser.go @@ -28,11 +28,13 @@ func Parse(s string) (*Semantic, error) { } func ParseApplication(s string) (*Application, error) { - if !strings.HasPrefix(s, "avalanche/") { + prefix := GetApplicationPrefix() + + if !strings.HasPrefix(s, prefix+"/") { return nil, fmt.Errorf("application string %q missing required prefix", s) } - s = s[10:] + s = s[(len(prefix) + 1):] major, minor, patch, err := parseVersions(s) if err != nil { return nil, err diff --git a/avalanchego/version/parser_test.go b/avalanchego/version/parser_test.go index 66bfb8f3..682cc053 100644 --- a/avalanchego/version/parser_test.go +++ b/avalanchego/version/parser_test.go @@ -6,6 +6,7 @@ package version import ( "testing" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/stretchr/testify/require" ) @@ -33,6 +34,8 @@ func TestParse(t *testing.T) { } func TestParseApplication(t *testing.T) { + InitApplicationPrefix(constants.FlareID) + v, err := ParseApplication("avalanche/1.2.3") require.NoError(t, err) diff --git a/coreth/consensus/dummy/consensus.go b/coreth/consensus/dummy/consensus.go index dd48bf94..65a169c4 100644 --- a/coreth/consensus/dummy/consensus.go +++ b/coreth/consensus/dummy/consensus.go @@ -163,8 +163,14 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade if config.IsApricotPhase5(timestamp) { blockGasCostStep = ApricotPhase5BlockGasCostStep } + var apricotPhase4TargetBlockRate uint64 + if config.IsSongbirdCode() { + apricotPhase4TargetBlockRate = SgbApricotPhase4TargetBlockRate + } else { + apricotPhase4TargetBlockRate = ApricotPhase4TargetBlockRate + } expectedBlockGasCost := calcBlockGasCost( - ApricotPhase4TargetBlockRate, + apricotPhase4TargetBlockRate, ApricotPhase4MinBlockGasCost, ApricotPhase4MaxBlockGasCost, blockGasCostStep, @@ -361,8 +367,14 @@ func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *type } // Calculate the expected blockGasCost for this block. // Note: this is a deterministic transtion that defines an exact block fee for this block. + var apricotPhase4TargetBlockRate uint64 + if chain.Config().IsSongbirdCode() { + apricotPhase4TargetBlockRate = SgbApricotPhase4TargetBlockRate + } else { + apricotPhase4TargetBlockRate = ApricotPhase4TargetBlockRate + } blockGasCost := calcBlockGasCost( - ApricotPhase4TargetBlockRate, + apricotPhase4TargetBlockRate, ApricotPhase4MinBlockGasCost, ApricotPhase4MaxBlockGasCost, blockGasCostStep, @@ -411,8 +423,14 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, blockGasCostStep = ApricotPhase5BlockGasCostStep } // Calculate the required block gas cost for this block. + var apricotPhase4TargetBlockRate uint64 + if chain.Config().IsSongbirdCode() { + apricotPhase4TargetBlockRate = SgbApricotPhase4TargetBlockRate + } else { + apricotPhase4TargetBlockRate = ApricotPhase4TargetBlockRate + } header.BlockGasCost = calcBlockGasCost( - ApricotPhase4TargetBlockRate, + apricotPhase4TargetBlockRate, ApricotPhase4MinBlockGasCost, ApricotPhase4MaxBlockGasCost, blockGasCostStep, diff --git a/coreth/consensus/dummy/dynamic_fees.go b/coreth/consensus/dummy/dynamic_fees.go index 527a46f3..a81e4e99 100644 --- a/coreth/consensus/dummy/dynamic_fees.go +++ b/coreth/consensus/dummy/dynamic_fees.go @@ -24,13 +24,14 @@ var ( ApricotPhase4BaseFeeChangeDenominator = new(big.Int).SetUint64(params.ApricotPhase4BaseFeeChangeDenominator) ApricotPhase5BaseFeeChangeDenominator = new(big.Int).SetUint64(params.ApricotPhase5BaseFeeChangeDenominator) - ApricotPhase3BlockGasFee uint64 = 1_000_000 - ApricotPhase4MinBlockGasCost = new(big.Int).Set(common.Big0) - ApricotPhase4MaxBlockGasCost = big.NewInt(1_000_000) - ApricotPhase4BlockGasCostStep = big.NewInt(50_000) - ApricotPhase4TargetBlockRate uint64 = 2 // in seconds - ApricotPhase5BlockGasCostStep = big.NewInt(200_000) - rollupWindow uint64 = 10 + ApricotPhase3BlockGasFee uint64 = 1_000_000 + ApricotPhase4MinBlockGasCost = new(big.Int).Set(common.Big0) + ApricotPhase4MaxBlockGasCost = big.NewInt(1_000_000) + ApricotPhase4BlockGasCostStep = big.NewInt(50_000) + ApricotPhase4TargetBlockRate uint64 = 2 // in seconds + SgbApricotPhase4TargetBlockRate uint64 = 1 // in seconds + ApricotPhase5BlockGasCostStep = big.NewInt(200_000) + rollupWindow uint64 = 10 ) // CalcBaseFee takes the previous header and the timestamp of its child block @@ -99,10 +100,17 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uin parentExtraStateGasUsed = parent.ExtDataGasUsed.Uint64() } case isApricotPhase4: + var apricotPhase4TargetBlockRate uint64 + if config.IsSongbirdCode() { + apricotPhase4TargetBlockRate = SgbApricotPhase4TargetBlockRate + } else { + apricotPhase4TargetBlockRate = ApricotPhase4TargetBlockRate + } + // The [blockGasCost] is paid by the effective tips in the block using // the block's value of [baseFee]. blockGasCost = calcBlockGasCost( - ApricotPhase4TargetBlockRate, + apricotPhase4TargetBlockRate, ApricotPhase4MinBlockGasCost, ApricotPhase4MaxBlockGasCost, ApricotPhase4BlockGasCostStep, From b9db9ac7db31218c6f7725d653fa5d16e9f3b67d Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 1 Apr 2024 21:36:24 +0200 Subject: [PATCH 16/39] added missing file --- avalanchego/version/flare_version.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 avalanchego/version/flare_version.go diff --git a/avalanchego/version/flare_version.go b/avalanchego/version/flare_version.go new file mode 100644 index 00000000..13dc6802 --- /dev/null +++ b/avalanchego/version/flare_version.go @@ -0,0 +1,18 @@ +package version + +import "github.com/ava-labs/avalanchego/utils/constants" + +var applicationPrefix = "avalanche" + +func InitApplicationPrefix(networkID uint32) { + if networkID == constants.CostonID || networkID == constants.SongbirdID || networkID == constants.LocalID { + applicationPrefix = "flare" + } +} + +func GetApplicationPrefix() string { + if applicationPrefix == "" { + panic("application prefix not set") + } + return applicationPrefix +} From 4ce58d1160d01164f1e2b7df7afb82d0bb7a4437 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 2 Apr 2024 12:13:31 +0200 Subject: [PATCH 17/39] updated coston and songbird c-chain genesis data --- coreth/params/config.go | 44 +++++++++++++++++++++++++++++++++++++++++ coreth/plugin/evm/vm.go | 10 +++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/coreth/params/config.go b/coreth/params/config.go index 53060010..316783a7 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -128,6 +128,50 @@ var ( BlueberryBlockTimestamp: big.NewInt(0), } + // CostonChainConfig is the configuration for the Coston test network. + CostonChainConfig = &ChainConfig{ + ChainID: CostonChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 15, 0, 0, 0, time.UTC).Unix()), + ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 16, 0, 0, 0, time.UTC).Unix()), + } + + // SongbirdChainConfig is the configuration for the Songbird canary network. + SongbirdChainConfig = &ChainConfig{ + ChainID: SongbirdChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 15, 0, 0, 0, time.UTC).Unix()), + ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 16, 0, 0, 0, time.UTC).Unix()), + } + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} TestLaunchConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil} TestApricotPhase1Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil} diff --git a/coreth/plugin/evm/vm.go b/coreth/plugin/evm/vm.go index 874e45ff..6a105844 100644 --- a/coreth/plugin/evm/vm.go +++ b/coreth/plugin/evm/vm.go @@ -381,6 +381,10 @@ func (vm *VM) Initialize( extDataHashes = fujiExtDataHashes case g.Config.ChainID.Cmp(params.AvalancheLocalChainID) == 0: g.Config = params.AvalancheLocalChainConfig + case g.Config.ChainID.Cmp(params.CostonChainID) == 0: + g.Config = params.CostonChainConfig + case g.Config.ChainID.Cmp(params.SongbirdChainID) == 0: + g.Config = params.SongbirdChainConfig } vm.syntacticBlockValidator = NewBlockValidator(extDataHashes) @@ -1060,10 +1064,10 @@ func (vm *VM) Version() (string, error) { } // NewHandler returns a new Handler for a service where: -// * The handler's functionality is defined by [service] +// - The handler's functionality is defined by [service] // [service] should be a gorilla RPC service (see https://www.gorillatoolkit.org/pkg/rpc/v2) -// * The name of the service is [name] -// * The LockOption is the first element of [lockOption] +// - The name of the service is [name] +// - The LockOption is the first element of [lockOption] // By default the LockOption is WriteLock // [lockOption] should have either 0 or 1 elements. Elements beside the first are ignored. func newHandler(name string, service interface{}, lockOption ...commonEng.LockOption) (*commonEng.HTTPHandler, error) { From 28a1aba98e173932f3c5fc444913988b29eb2b79 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 15:45:20 +0200 Subject: [PATCH 18/39] fixed wrong block verification in songbird in phase 5 --- coreth/plugin/evm/block_verification.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreth/plugin/evm/block_verification.go b/coreth/plugin/evm/block_verification.go index 35efa39c..621d1e8d 100644 --- a/coreth/plugin/evm/block_verification.go +++ b/coreth/plugin/evm/block_verification.go @@ -101,7 +101,7 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { if rules.IsSongbirdCode { // SGB-MERGE - if ethHeader.GasLimit != params.SgbApricotPhase5GasLimit { + if rules.IsApricotPhase5 && ethHeader.GasLimit != params.SgbApricotPhase5GasLimit { return fmt.Errorf( "expected gas limit to be %d in apricot phase 5 but got %d", params.SgbApricotPhase5GasLimit, ethHeader.GasLimit, From e74308fee417b1c3539b2ee12e9083363fb00280 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 16:49:56 +0200 Subject: [PATCH 19/39] fixed wrong gas verification in songbird in phase 5 --- coreth/consensus/dummy/consensus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreth/consensus/dummy/consensus.go b/coreth/consensus/dummy/consensus.go index 65a169c4..a968a56a 100644 --- a/coreth/consensus/dummy/consensus.go +++ b/coreth/consensus/dummy/consensus.go @@ -99,7 +99,7 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade if config.IsSongbirdCode() { // SGB-MERGE // Verify that the gas limit is correct for the current phase - if header.GasLimit != params.SgbApricotPhase5GasLimit { + if config.IsApricotPhase5(timestamp) && header.GasLimit != params.SgbApricotPhase5GasLimit { return fmt.Errorf("expected gas limit to be %d in apricot phase 5 but got %d", params.SgbApricotPhase5GasLimit, header.GasLimit) } } else { From 72156de5a02d437b678218442233e474babb7b51 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 17:53:27 +0200 Subject: [PATCH 20/39] fixed mismatch between flare and songbird code in transitionDb --- coreth/core/state_transition.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index dd64582b..ab0417b7 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -371,7 +371,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { chainID = st.evm.ChainConfig().ChainID timestamp = st.evm.Context.Time - burnAddress, nominalGasPrice, isSongbird, isFlare, err := stateTransitionVariants.GetValue(chainID)(st) + burnAddress, nominalGasPrice, isFlare, isSongbird, err := stateTransitionVariants.GetValue(chainID)(st) if err != nil { return nil, err } From 0126dfc4eb502d30221d0f94b95ce24218d171bb Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 18:26:30 +0200 Subject: [PATCH 21/39] fixed mismatch between flare and songbird mint request size --- coreth/core/daemon.go | 10 +++++++--- coreth/core/state_transition.go | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index b010ffa4..bd355efa 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -104,6 +104,7 @@ func (e *ErrMintNegative) Error() string { return "mint request cannot be negati // Define interface for dependencies type EVMCaller interface { + GetChainID() *big.Int DaemonCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64) (snapshot int, ret []byte, leftOverGas uint64, err error) DaemonRevertToSnapshot(snapshot int) GetBlockTime() *big.Int @@ -151,11 +152,14 @@ func IsPrioritisedContractCall(chainID *big.Int, blockTime *big.Int, to *common. } } -func GetMaximumMintRequest(blockTime *big.Int) *big.Int { +func GetMaximumMintRequest(chainID *big.Int, blockTime *big.Int) *big.Int { switch { - default: + case chainID.Cmp(params.FlareChainID) == 0 || chainID.Cmp(params.CostwoChainID) == 0 || chainID.Cmp(params.LocalFlareChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0: maxRequest, _ := new(big.Int).SetString("60000000000000000000000000", 10) return maxRequest + default: // Songbird, Coston + maxRequest, _ := new(big.Int).SetString("50000000000000000000000000", 10) + return maxRequest } } @@ -194,7 +198,7 @@ func daemon(evm EVMCaller) (int, *big.Int, error) { func mint(evm EVMCaller, mintRequest *big.Int) error { // If the mint request is greater than zero and less than max - max := GetMaximumMintRequest(evm.GetBlockTime()) + max := GetMaximumMintRequest(evm.GetChainID(), evm.GetBlockTime()) if mintRequest.Cmp(big.NewInt(0)) > 0 && mintRequest.Cmp(max) <= 0 { // Mint the amount asked for on to the daemon contract diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index ab0417b7..672e33f2 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -138,6 +138,10 @@ func (st *StateTransition) DaemonRevertToSnapshot(snapshot int) { st.evm.StateDB.RevertToSnapshot(snapshot) } +func (st *StateTransition) GetChainID() *big.Int { + return st.evm.ChainConfig().ChainID +} + func (st *StateTransition) GetBlockTime() *big.Int { return st.evm.Context.Time } From f42f752196baa82fca9ecdc24e0ee25a13366513 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 19:16:13 +0200 Subject: [PATCH 22/39] fixed mismatch between flare and songbird apricot 5 gas limit --- coreth/core/chain_makers.go | 8 +++++++- coreth/core/daemon_test.go | 18 +++++++++++++++++- coreth/miner/worker.go | 8 +++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/coreth/core/chain_makers.go b/coreth/core/chain_makers.go index 9fb78557..ee0bdffc 100644 --- a/coreth/core/chain_makers.go +++ b/coreth/core/chain_makers.go @@ -292,7 +292,13 @@ func makeHeader(chain consensus.ChainReader, config *params.ChainConfig, parent var gasLimit uint64 if config.IsSongbirdCode() { // SGB-MERGE - gasLimit = params.SgbApricotPhase5GasLimit + if config.IsApricotPhase5(timestamp) { + gasLimit = params.SgbApricotPhase5GasLimit + } else if config.IsApricotPhase1(timestamp) { + gasLimit = params.ApricotPhase1GasLimit + } else { + gasLimit = CalcGasLimit(parent.GasUsed(), parent.GasLimit(), parent.GasLimit(), parent.GasLimit()) + } } else { if config.IsApricotPhase1(timestamp) { gasLimit = params.ApricotPhase1GasLimit diff --git a/coreth/core/daemon_test.go b/coreth/core/daemon_test.go index 756c8e13..619e60af 100644 --- a/coreth/core/daemon_test.go +++ b/coreth/core/daemon_test.go @@ -82,6 +82,10 @@ func (e *DefaultEVMMock) GetGasLimit() uint64 { return defaultGetGasLimit(&e.mockEVMCallerData) } +func (e *DefaultEVMMock) GetChainID() *big.Int { + return params.FlareChainID +} + func (e *DefaultEVMMock) AddBalance(addr common.Address, amount *big.Int) { defaultAddBalance(&e.mockEVMCallerData, addr, amount) } @@ -158,6 +162,10 @@ func (e *BadMintReturnSizeEVMMock) GetGasLimit() uint64 { return defaultGetGasLimit(&e.mockEVMCallerData) } +func (e *BadMintReturnSizeEVMMock) GetChainID() *big.Int { + return params.FlareChainID +} + func (e *BadMintReturnSizeEVMMock) AddBalance(addr common.Address, amount *big.Int) { defaultAddBalance(&e.mockEVMCallerData, addr, amount) } @@ -213,6 +221,10 @@ func (e *BadDaemonCallEVMMock) GetGasLimit() uint64 { return defaultGetGasLimit(&e.mockEVMCallerData) } +func (e *BadDaemonCallEVMMock) GetChainID() *big.Int { + return params.FlareChainID +} + func (e *BadDaemonCallEVMMock) AddBalance(addr common.Address, amount *big.Int) { defaultAddBalance(&e.mockEVMCallerData, addr, amount) } @@ -304,6 +316,10 @@ func (e *ReturnNilMintRequestEVMMock) GetGasLimit() uint64 { return defaultGetGasLimit(&e.mockEVMCallerData) } +func (e *ReturnNilMintRequestEVMMock) GetChainID() *big.Int { + return params.FlareChainID +} + func (e *ReturnNilMintRequestEVMMock) AddBalance(addr common.Address, amount *big.Int) { defaultAddBalance(&e.mockEVMCallerData, addr, amount) } @@ -343,7 +359,7 @@ func TestDaemonShouldNotMintMoreThanMax(t *testing.T) { if err, ok := err.(*ErrMaxMintExceeded); !ok { want := &ErrMaxMintExceeded{ mintRequest: mintRequest, - mintMax: GetMaximumMintRequest(big.NewInt(0)), + mintMax: GetMaximumMintRequest(params.FlareChainID, big.NewInt(0)), } t.Errorf("got '%s' want '%s'", err.Error(), want.Error()) } diff --git a/coreth/miner/worker.go b/coreth/miner/worker.go index d72a185d..78cd9ab4 100644 --- a/coreth/miner/worker.go +++ b/coreth/miner/worker.go @@ -129,7 +129,13 @@ func (w *worker) commitNewWork() (*types.Block, error) { var gasLimit uint64 if w.chainConfig.IsSongbirdCode() { - gasLimit = params.SgbApricotPhase5GasLimit + if w.chainConfig.IsApricotPhase5(big.NewInt(timestamp)) { + gasLimit = params.SgbApricotPhase5GasLimit + } else if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { + gasLimit = params.ApricotPhase1GasLimit + } else { + gasLimit = core.CalcGasLimit(parent.GasUsed(), parent.GasLimit(), params.ApricotPhase1GasLimit, params.ApricotPhase1GasLimit) + } } else { if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { gasLimit = params.ApricotPhase1GasLimit From 3074115c87a45a06d03730c85923b2d0a7981494 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 20:58:15 +0200 Subject: [PATCH 23/39] fixed songbird and coston attestors --- coreth/core/state_connector.go | 50 ++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/coreth/core/state_connector.go b/coreth/core/state_connector.go index e4e8f588..6d922b16 100644 --- a/coreth/core/state_connector.go +++ b/coreth/core/state_connector.go @@ -221,8 +221,35 @@ func GetDefaultAttestors(chainID *big.Int, blockTime *big.Int) []common.Address } } case chainID.Cmp(params.SongbirdChainID) == 0: - return []common.Address{ - common.HexToAddress("0x0c19f3B4927abFc596353B0f9Ddad5D817736F70"), + switch { + case blockTime.Cmp(submitterContractActivationTimeSongbird) > 0: + return []common.Address{ + common.HexToAddress("0xcE397b9a395ace2e328030699bDDf4E2F049A05B"), + common.HexToAddress("0xeDBb013BBC314124a9f842c1887e34cfeB03B052"), + common.HexToAddress("0xb9eF3951ac2D04C6bdD886bF042041E3954E86aF"), + common.HexToAddress("0x816Cec8f3A37Fd673Cfd4229441c59cA8DbD0641"), + common.HexToAddress("0x14c9c4583F0b1af8a69452Ec1b29884240f83bDC"), + common.HexToAddress("0x0049081C2D6def64800cC011Bd9aDe8682c6593a"), + common.HexToAddress("0x53Fcb50a22aFd6e5438d754CB22c4726032d2488"), + common.HexToAddress("0x35f4F0Bb73a6040F24927e1735B089d7769F7674"), + common.HexToAddress("0x3B583C919fD4C863F3A17d11929346C687FfB7c3"), + } + case blockTime.Cmp(songbirdOct22ForkTime) > 0: + return []common.Address{ + common.HexToAddress("0x2D3e7e4b19bDc920fd9C57BD3072A31F5a59FeC8"), + common.HexToAddress("0x442DD539Fe78D43A1a9358FF3460CfE63e2bC9CC"), + common.HexToAddress("0x49893c5Dfc035F4eE4E46faC014f6D4bC80F7f92"), + common.HexToAddress("0x5D2f75392DdDa69a2818021dd6a64937904c8352"), + common.HexToAddress("0x6455dC38fdF739b6fE021b30C7D9672C1c6DEb5c"), + common.HexToAddress("0x808441Ec3Fa1721330226E69527Bc160D8d9386a"), + common.HexToAddress("0x823B0f5c7758E9d3bE55bA1EA840E29ccd5D5CcB"), + common.HexToAddress("0x85016969b9eBDB8977975a4743c9FCEeabCEAf8A"), + common.HexToAddress("0x8A3D627D86A81F5D21683F4963565C63DB5e1309"), + } + default: + return []common.Address{ + common.HexToAddress("0x0c19f3B4927abFc596353B0f9Ddad5D817736F70"), + } } case chainID.Cmp(params.CostwoChainID) == 0: return []common.Address{ @@ -236,6 +263,25 @@ func GetDefaultAttestors(chainID *big.Int, blockTime *big.Int) []common.Address common.HexToAddress("0x3d895D00d2802120D39d4D2554F7ef09d6845E99"), common.HexToAddress("0xc36141CFBe5Af6eB2F8b21550Ccd457DA7FaF3C6"), } + case chainID.Cmp(params.CostonChainID) == 0: + switch { + case blockTime.Cmp(costonOct22ForkTime) > 0: + return []common.Address{ + common.HexToAddress("0x30e4b4542b4aAf615838B113f14c46dE1469212e"), + common.HexToAddress("0x3519E14183252794aaA52aA824f34482ef44cE1d"), + common.HexToAddress("0xb445857476181ec378Ec453ab3d122183CfC3b78"), + common.HexToAddress("0x6D755cd7A61A9DCFc96FaE0f927C3a73bE986ce4"), + common.HexToAddress("0xdC0fD24846303D58d2D66AA8820be2685735dBd2"), + common.HexToAddress("0x3F52c41c0500a4f018A38c9f8273b254aD7e2FCc"), + common.HexToAddress("0xdA6d6aA9F1f770c279c5DA0C71f4DC1142A70d5D"), + common.HexToAddress("0x3d895D00d2802120D39d4D2554F7ef09d6845E99"), + common.HexToAddress("0xc36141CFBe5Af6eB2F8b21550Ccd457DA7FaF3C6"), + } + default: + return []common.Address{ + common.HexToAddress("0x3a6e101103ec3d9267d08f484a6b70e1440a8255"), + } + } case chainID.Cmp(params.StagingChainID) == 0: return []common.Address{ common.HexToAddress("0x0988Cf4828F4e4eD0cE7c07467E70e19095Ee152"), From 18a2fa7a72b11534d07e121066674641dca7da68 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 21:32:10 +0200 Subject: [PATCH 24/39] fixed mismatch between DefaultMinGasUsed on flare and songbird --- coreth/eth/ethconfig/config.go | 30 ++++++++++++++++++++++++++++++ coreth/eth/gasprice/gasprice.go | 9 +++++++-- coreth/plugin/evm/vm.go | 6 +++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/coreth/eth/ethconfig/config.go b/coreth/eth/ethconfig/config.go index 11bfc197..63cf965a 100644 --- a/coreth/eth/ethconfig/config.go +++ b/coreth/eth/ethconfig/config.go @@ -46,6 +46,17 @@ var DefaultFullGPOConfig = gasprice.Config{ MinGasUsed: gasprice.DefaultMinGasUsed, } +var DefaultFullGPOSgbConfig = gasprice.Config{ + Blocks: 40, + Percentile: 60, + MaxLookbackSeconds: gasprice.DefaultMaxLookbackSeconds, + MaxCallBlockHistory: gasprice.DefaultMaxCallBlockHistory, + MaxBlockHistory: gasprice.DefaultMaxBlockHistory, + MinPrice: gasprice.DefaultMinPrice, + MaxPrice: gasprice.DefaultMaxPrice, + MinGasUsed: gasprice.SgbDefaultMinGasUsed, +} + // DefaultConfig contains default settings for use on the Avalanche main net. var DefaultConfig = NewDefaultConfig() @@ -68,6 +79,25 @@ func NewDefaultConfig() Config { } } +func NewDefaultSgbConfig() Config { + return Config{ + NetworkId: 1, + LightPeers: 100, + UltraLightFraction: 75, + DatabaseCache: 512, + TrieCleanCache: 256, + TrieDirtyCache: 256, + TrieDirtyCommitTarget: 20, + SnapshotCache: 128, + Miner: miner.Config{}, + TxPool: core.DefaultTxPoolConfig, + RPCGasCap: 25000000, + RPCEVMTimeout: 5 * time.Second, + GPO: DefaultFullGPOSgbConfig, + RPCTxFeeCap: 1, // 1 AVAX + } +} + //go:generate go run github.com/fjl/gencodec -type Config -formats toml -out gen_config.go // Config contains configuration options for of the ETH and LES protocols. diff --git a/coreth/eth/gasprice/gasprice.go b/coreth/eth/gasprice/gasprice.go index 2abcc637..94229743 100644 --- a/coreth/eth/gasprice/gasprice.go +++ b/coreth/eth/gasprice/gasprice.go @@ -68,7 +68,8 @@ var ( DefaultMaxPrice = big.NewInt(150 * params.GWei) DefaultMinPrice = big.NewInt(0 * params.GWei) DefaultMinBaseFee = big.NewInt(params.ApricotPhase3InitialBaseFee) - DefaultMinGasUsed = big.NewInt(6_000_000) // block gas limit is 8,000,000 + DefaultMinGasUsed = big.NewInt(6_000_000) // block gas limit is 8,000,000 + SgbDefaultMinGasUsed = big.NewInt(12_000_000) // block gas target is 15,000,000 DefaultMaxLookbackSeconds = uint64(80) ) @@ -165,7 +166,11 @@ func NewOracle(backend OracleBackend, config Config) *Oracle { } minGasUsed := config.MinGasUsed if minGasUsed == nil || minGasUsed.Int64() < 0 { - minGasUsed = DefaultMinGasUsed + if backend.ChainConfig().IsSongbirdCode() { + minGasUsed = SgbDefaultMinGasUsed + } else { + minGasUsed = DefaultMinGasUsed + } log.Warn("Sanitizing invalid gasprice oracle min gas used", "provided", config.MinGasUsed, "updated", minGasUsed) } maxCallBlockHistory := config.MaxCallBlockHistory diff --git a/coreth/plugin/evm/vm.go b/coreth/plugin/evm/vm.go index 6a105844..97aaab47 100644 --- a/coreth/plugin/evm/vm.go +++ b/coreth/plugin/evm/vm.go @@ -405,7 +405,11 @@ func (vm *VM) Initialize( vm.chainID = g.Config.ChainID - vm.ethConfig = ethconfig.NewDefaultConfig() + if g.Config.IsSongbirdCode() { + vm.ethConfig = ethconfig.NewDefaultSgbConfig() + } else { + vm.ethConfig = ethconfig.NewDefaultConfig() + } vm.ethConfig.Genesis = g vm.ethConfig.NetworkId = vm.chainID.Uint64() From 033a40823215b5b0b8db3d278ee84e1790c0f3dc Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 3 Apr 2024 22:10:49 +0200 Subject: [PATCH 25/39] fixed state connector coinbase signal address for songbird --- coreth/core/state_connector.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/coreth/core/state_connector.go b/coreth/core/state_connector.go index 6d922b16..bb1d949f 100644 --- a/coreth/core/state_connector.go +++ b/coreth/core/state_connector.go @@ -156,8 +156,24 @@ func GetStateConnectorIsActivatedAndCalledCoston(blockTime *big.Int, to common.A func GetStateConnectorCoinbaseSignalAddr(chainID *big.Int, blockTime *big.Int) common.Address { switch { - default: + case chainID.Cmp(params.FlareChainID) == 0 || chainID.Cmp(params.CostwoChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0 || chainID.Cmp(params.LocalFlareChainID) == 0: return common.HexToAddress("0x00000000000000000000000000000000000DEaD1") + case chainID.Cmp(params.SongbirdChainID) == 0: + switch { + case blockTime.Cmp(songbirdOct22ForkTime) > 0: + return common.HexToAddress("0x00000000000000000000000000000000000DEaD1") + default: + return common.HexToAddress("0x000000000000000000000000000000000000dEaD") + } + case chainID.Cmp(params.CostonChainID) == 0: + switch { + case blockTime.Cmp(costonOct22ForkTime) > 0: + return common.HexToAddress("0x00000000000000000000000000000000000DEaD1") + default: + return common.HexToAddress("0x000000000000000000000000000000000000dEaD") + } + default: + return common.HexToAddress("0x000000000000000000000000000000000000dEaD") } } From 5c31f391f1b254b80316d17b225626c4dac8a3c7 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 16 Apr 2024 22:52:38 +0400 Subject: [PATCH 26/39] Changes to tests --- avalanchego/genesis/genesis_test.go | 10 ++++++---- coreth/core/state_transition_params.go | 11 ++++++++--- coreth/params/config.go | 2 +- coreth/utils/flare_choice.go | 6 ++++-- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/avalanchego/genesis/genesis_test.go b/avalanchego/genesis/genesis_test.go index 6a315f67..828f4b48 100644 --- a/avalanchego/genesis/genesis_test.go +++ b/avalanchego/genesis/genesis_test.go @@ -344,8 +344,9 @@ func TestGenesis(t *testing.T) { expectedID: "UUvXi6j7QhVvgpbKM89MP5HdrxKm9CaJeHc187TsDNf8nZdLk", }, { - networkID: constants.SongbirdID, - expectedID: "wehzwSstW6ChWVj356tLr6sJfSb8PaZMhcerXUyxsAUzia1Gr", + networkID: constants.SongbirdID, + // expectedID: "wehzwSstW6ChWVj356tLr6sJfSb8PaZMhcerXUyxsAUzia1Gr", + expectedID: "2r5gyF26aYN9BHaFYaDDFGpx2tiq4bz3GCiYUGo4QzKpzHeX9g", }, { networkID: constants.LocalID, @@ -396,8 +397,9 @@ func TestVMGenesis(t *testing.T) { expectedID: "7xKYhEvYuUekwDxozgEiMPufzJ3jJPypKbGE8ny6KL84z4RKB", }, { - vmID: constants.EVMID, - expectedID: "2aamPVJ7SXz7DBUxFNY1Lyga52KWaVUH2Gz9Fetxo5gwjY5a29", + vmID: constants.EVMID, + // expectedID: "2aamPVJ7SXz7DBUxFNY1Lyga52KWaVUH2Gz9Fetxo5gwjY5a29", + expectedID: "n5Q3QkMnpoq9h6nA36WrL5sH7fDNcyCBkMtP1r9He7AcQeR7y", }, }, }, diff --git a/coreth/core/state_transition_params.go b/coreth/core/state_transition_params.go index ebd8315b..e22ed1e2 100644 --- a/coreth/core/state_transition_params.go +++ b/coreth/core/state_transition_params.go @@ -10,13 +10,18 @@ import ( ) var ( - stateTransitionVariants = utils.NewChainValue(invalidChain). + stateTransitionVariants = utils.NewChainValue(nonFlareChain). AddValues([]*big.Int{params.FlareChainID, params.CostwoChainID, params.StagingChainID, params.LocalFlareChainID}, stateTransitionParamsFlare). AddValues([]*big.Int{params.SongbirdChainID, params.CostonChainID, params.LocalChainID}, stateTransitionParamsSongbird) ) -func invalidChain(st *StateTransition) (common.Address, uint64, bool, bool, error) { - return common.Address{}, 0, false, false, errors.New("invalid chain ID") +// Used in tests +func nonFlareChain(st *StateTransition) (common.Address, uint64, bool, bool, error) { + return common.HexToAddress("0x000000000000000000000000000000000000dEaD"), + uint64(params.ApricotPhase4MinBaseFee), + false, + false, + nil } // Returns the state transition parameters for the given chain ID diff --git a/coreth/params/config.go b/coreth/params/config.go index 316783a7..588b8ada 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -262,7 +262,7 @@ func (c *ChainConfig) String() string { // SGB-MERGE // Code for songbird network (songbird, coston, local id) func (c *ChainConfig) IsSongbirdCode() bool { - return c.ChainID.Cmp(SongbirdChainID) == 0 || c.ChainID.Cmp(CostonChainID) == 0 || c.ChainID.Cmp(LocalChainID) == 0 + return c.ChainID != nil && (c.ChainID.Cmp(SongbirdChainID) == 0 || c.ChainID.Cmp(CostonChainID) == 0 || c.ChainID.Cmp(LocalChainID) == 0) } // IsHomestead returns whether num is either equal to the homestead block or greater. diff --git a/coreth/utils/flare_choice.go b/coreth/utils/flare_choice.go index 7279d065..08834313 100644 --- a/coreth/utils/flare_choice.go +++ b/coreth/utils/flare_choice.go @@ -27,8 +27,10 @@ func (ca *ChainValue[T]) AddValues(chainIDs []*big.Int, action T) *ChainValue[T] } func (ca *ChainValue[T]) GetValue(chainID *big.Int) T { - if action, ok := ca.valueMap[chainID.Uint64()]; ok { - return action + if chainID != nil { + if action, ok := ca.valueMap[chainID.Uint64()]; ok { + return action + } } return ca.defaultValue } From dc7a70367e805f795493a3aba3764ec9a7c6c508 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 22 Apr 2024 17:07:56 +0200 Subject: [PATCH 27/39] Disabled export/import transactions before songbird upgrade time --- .../txs/executor/inflation_settings.go | 49 +++++++++++++++++-- coreth/params/config.go | 36 +++++++++----- coreth/plugin/evm/export_tx.go | 7 ++- coreth/plugin/evm/import_tx.go | 5 ++ coreth/plugin/evm/vm.go | 2 + 5 files changed, 83 insertions(+), 16 deletions(-) diff --git a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go index dbfc8e4c..650c6080 100644 --- a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go +++ b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go @@ -14,6 +14,8 @@ var inflationSettingsVariants = utils.NewNetworkValue(getDefaultInflationSetting AddValue(constants.CostwoID, getCostwoInflationSettings). AddValue(constants.LocalFlareID, getLocalFlareInflationSettings). AddValue(constants.StagingID, getStagingInflationSettings). + AddValue(constants.SongbirdID, getSongbirdInflationSettings). + AddValue(constants.CostonID, getCostonInflationSettings). AddValue(constants.LocalID, getLocalInflationSettings) type InflationSettings struct { @@ -150,13 +152,52 @@ func getStagingInflationSettings(_ time.Time, _ *config.Config) InflationSetting } } -func getLocalInflationSettings(currentTimestamp time.Time, config *config.Config) InflationSettings { +func getSongbirdInflationSettings(currentTimestamp time.Time, config *config.Config) InflationSettings { switch { case currentTimestamp.Before(time.Date(2000, time.March, 1, 0, 0, 0, 0, time.UTC)): - // Phase 1 return getDefaultInflationSettings(currentTimestamp, config) default: // Phase 2 + return InflationSettings{ + MinValidatorStake: 1 * units.MegaAvax, + MaxValidatorStake: 200 * units.MegaAvax, + MinDelegatorStake: 50 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 60 * 24 * time.Hour, + MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: 15, + MinStakeStartTime: time.Date(2024, time.October, 1, 0, 0, 0, 0, time.UTC), + } + } +} + +func getCostonInflationSettings(currentTimestamp time.Time, config *config.Config) InflationSettings { + switch { + case currentTimestamp.Before(time.Date(2000, time.March, 1, 0, 0, 0, 0, time.UTC)): + return getDefaultInflationSettings(currentTimestamp, config) + default: + return InflationSettings{ + MinValidatorStake: 100 * units.KiloAvax, + MaxValidatorStake: 50 * units.MegaAvax, + MinDelegatorStake: 10 * units.KiloAvax, + MinDelegationFee: 0, + MinStakeDuration: 24 * time.Hour, + MinDelegateDuration: 1 * time.Hour, + MaxStakeDuration: 365 * 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, + MaxValidatorWeightFactor: 15, + MinStakeStartTime: time.Date(2024, time.April, 24, 15, 0, 0, 0, time.UTC), + } + } +} + +func getLocalInflationSettings(currentTimestamp time.Time, config *config.Config) InflationSettings { + switch { + case currentTimestamp.Before(time.Date(2000, time.March, 1, 0, 0, 0, 0, time.UTC)): + return getDefaultInflationSettings(currentTimestamp, config) + default: return InflationSettings{ MinValidatorStake: 10 * units.KiloAvax, MaxValidatorStake: 50 * units.MegaAvax, @@ -166,8 +207,8 @@ func getLocalInflationSettings(currentTimestamp time.Time, config *config.Config MinDelegateDuration: 20 * time.Minute, MaxStakeDuration: 365 * 24 * time.Hour, MinFutureStartTimeOffset: MaxFutureStartTime, - MaxValidatorWeightFactor: MaxValidatorWeightFactor, - MinStakeStartTime: time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC), + MaxValidatorWeightFactor: 15, + MinStakeStartTime: time.Date(2024, time.April, 22, 15, 0, 0, 0, time.UTC), } } } diff --git a/coreth/params/config.go b/coreth/params/config.go index 588b8ada..a21765a3 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -148,6 +148,7 @@ var ( ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 14, 0, 0, 0, time.UTC).Unix()), ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 15, 0, 0, 0, time.UTC).Unix()), ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 16, 0, 0, 0, time.UTC).Unix()), + SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.April, 25, 0, 0, 0, 0, time.UTC).Unix()), } // SongbirdChainConfig is the configuration for the Songbird canary network. @@ -170,16 +171,17 @@ var ( ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 14, 0, 0, 0, time.UTC).Unix()), ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 15, 0, 0, 0, time.UTC).Unix()), ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 16, 0, 0, 0, time.UTC).Unix()), + SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.May, 25, 0, 0, 0, 0, time.UTC).Unix()), } - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} - TestLaunchConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil} - TestApricotPhase1Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil} - TestApricotPhase2Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil} - TestApricotPhase3Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil} - TestApricotPhase4Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil} - TestApricotPhase5Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil} - TestBlueberryChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} + TestLaunchConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil} + TestApricotPhase1Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil} + TestApricotPhase2Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil} + TestApricotPhase3Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil} + TestApricotPhase4Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil} + TestApricotPhase5Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil} + TestBlueberryChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil} TestRules = TestChainConfig.AvalancheRules(new(big.Int), new(big.Int)) ) @@ -222,6 +224,9 @@ type ChainConfig struct { ApricotPhase5BlockTimestamp *big.Int `json:"apricotPhase5BlockTimestamp,omitempty"` // Blueberry TODO comment. (nil = no fork, 0 = already activated) BlueberryBlockTimestamp *big.Int `json:"blueberryBlockTimestamp,omitempty"` + + // SGB-MERGE: when export/import transactions will be allowed on songbird code (Songbird, Coston, Local) + SongbirdTransitionTimestamp *big.Int `json:"songbirdTransitionTimestamp,omitempty"` } // String implements the fmt.Stringer interface. @@ -255,12 +260,13 @@ func (c *ChainConfig) String() string { banner += fmt.Sprintf(" - Apricot Phase 4 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.6.0)\n", c.ApricotPhase4BlockTimestamp) banner += fmt.Sprintf(" - Apricot Phase 5 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.7.0)\n", c.ApricotPhase5BlockTimestamp) banner += fmt.Sprintf(" - Bluberry Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.8.0)\n", c.BlueberryBlockTimestamp) + banner += fmt.Sprintf(" - Sgb Transition Timestamp: %-8v\n", c.SongbirdTransitionTimestamp) banner += "\n" return banner } // SGB-MERGE -// Code for songbird network (songbird, coston, local id) +// Code for songbird network (Songbird, Coston, Local id) func (c *ChainConfig) IsSongbirdCode() bool { return c.ChainID != nil && (c.ChainID.Cmp(SongbirdChainID) == 0 || c.ChainID.Cmp(CostonChainID) == 0 || c.ChainID.Cmp(LocalChainID) == 0) } @@ -355,6 +361,12 @@ func (c *ChainConfig) IsBlueberry(blockTimestamp *big.Int) bool { return utils.IsForked(c.BlueberryBlockTimestamp, blockTimestamp) } +// IsSongbirdTransition returns whether [blockTimestamp] represents a block +// with a timestamp after the Songbird code transition time. +func (c *ChainConfig) IsSongbirdTransition(blockTimestamp *big.Int) bool { + return utils.IsForked(c.SongbirdTransitionTimestamp, blockTimestamp) +} + // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, timestamp uint64) *ConfigCompatError { @@ -579,8 +591,9 @@ type Rules struct { IsBlueberry bool // SGB-MERGE - // Songbird (coston, local) - IsSongbirdCode bool + // Songbird (Songbird, Coston, Local) + IsSongbirdCode bool + IsSongbirdTransition bool // Precompiles maps addresses to stateful precompiled contracts that are enabled // for this rule set. @@ -620,6 +633,7 @@ func (c *ChainConfig) AvalancheRules(blockNum, blockTimestamp *big.Int) Rules { rules.IsApricotPhase4 = c.IsApricotPhase4(blockTimestamp) rules.IsApricotPhase5 = c.IsApricotPhase5(blockTimestamp) rules.IsBlueberry = c.IsBlueberry(blockTimestamp) + rules.IsSongbirdTransition = c.IsSongbirdTransition(blockTimestamp) // Initialize the stateful precompiles that should be enabled at [blockTimestamp]. rules.Precompiles = make(map[common.Address]precompile.StatefulPrecompiledContract) diff --git a/coreth/plugin/evm/export_tx.go b/coreth/plugin/evm/export_tx.go index 87943912..7865e867 100644 --- a/coreth/plugin/evm/export_tx.go +++ b/coreth/plugin/evm/export_tx.go @@ -75,6 +75,11 @@ func (utx *UnsignedExportTx) Verify( return errWrongBlockchainID } + // SGB-MERGE + if rules.IsSongbirdCode && !rules.IsSongbirdTransition { + return errExportTxsDisabled + } + // Make sure that the tx has a valid peer chain ID if rules.IsApricotPhase5 { // Note that SameSubnet verifies that [tx.DestinationChain] isn't this @@ -165,7 +170,7 @@ func (utx *UnsignedExportTx) Burned(assetID ids.ID) (uint64, error) { func (utx *UnsignedExportTx) SemanticVerify( vm *VM, stx *Tx, - _ *Block, + parent *Block, baseFee *big.Int, rules params.Rules, ) error { diff --git a/coreth/plugin/evm/import_tx.go b/coreth/plugin/evm/import_tx.go index bc433315..e9c0803e 100644 --- a/coreth/plugin/evm/import_tx.go +++ b/coreth/plugin/evm/import_tx.go @@ -69,6 +69,11 @@ func (utx *UnsignedImportTx) Verify( return errNoEVMOutputs } + // SGB-MERGE + if rules.IsSongbirdCode && !rules.IsSongbirdTransition { + return errImportTxsDisabled + } + // Make sure that the tx has a valid peer chain ID if rules.IsApricotPhase5 { // Note that SameSubnet verifies that [tx.SourceChain] isn't this diff --git a/coreth/plugin/evm/vm.go b/coreth/plugin/evm/vm.go index 97aaab47..f2c72baa 100644 --- a/coreth/plugin/evm/vm.go +++ b/coreth/plugin/evm/vm.go @@ -175,6 +175,8 @@ var ( errConflictingAtomicTx = errors.New("conflicting atomic tx present") errTooManyAtomicTx = errors.New("too many atomic tx") errMissingAtomicTxs = errors.New("cannot build a block with non-empty extra data and zero atomic transactions") + errImportTxsDisabled = errors.New("import transactions are disabled") + errExportTxsDisabled = errors.New("export transactions are disabled") ) var originalStderr *os.File From a3f4cc434c99d2f490ff2593d3e21c26cf18ae41 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 22 Apr 2024 18:01:55 +0200 Subject: [PATCH 28/39] Changes to Local Chain config --- coreth/params/config.go | 23 +++++++++++++++++++++++ coreth/plugin/evm/vm.go | 2 ++ 2 files changed, 25 insertions(+) diff --git a/coreth/params/config.go b/coreth/params/config.go index a21765a3..361f3861 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -151,6 +151,29 @@ var ( SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.April, 25, 0, 0, 0, 0, time.UTC).Unix()), } + // LocalChainConfig is the configuration for the Songbird Local network. + LocalChainConfig = &ChainConfig{ + ChainID: LocalChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + SongbirdTransitionTimestamp: big.NewInt(0), + } + // SongbirdChainConfig is the configuration for the Songbird canary network. SongbirdChainConfig = &ChainConfig{ ChainID: SongbirdChainID, diff --git a/coreth/plugin/evm/vm.go b/coreth/plugin/evm/vm.go index f2c72baa..887d7a42 100644 --- a/coreth/plugin/evm/vm.go +++ b/coreth/plugin/evm/vm.go @@ -387,6 +387,8 @@ func (vm *VM) Initialize( g.Config = params.CostonChainConfig case g.Config.ChainID.Cmp(params.SongbirdChainID) == 0: g.Config = params.SongbirdChainConfig + case g.Config.ChainID.Cmp(params.LocalChainID) == 0: + g.Config = params.LocalChainConfig } vm.syntacticBlockValidator = NewBlockValidator(extDataHashes) From 4b86b5cbd8beaf164324b1f84635ff04cccb201b Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Wed, 24 Apr 2024 17:59:33 +0200 Subject: [PATCH 29/39] Fixed wrong songbird genesis --- avalanchego/genesis/genesis_songbird.go | 2 +- avalanchego/genesis/genesis_test.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/avalanchego/genesis/genesis_songbird.go b/avalanchego/genesis/genesis_songbird.go index 946bd198..4d193b3c 100644 --- a/avalanchego/genesis/genesis_songbird.go +++ b/avalanchego/genesis/genesis_songbird.go @@ -94,7 +94,7 @@ var ( "4AC175dcf8355A5Ed545a7178715c542cF43B9bB": {"balance": "0x2116545850052128000000"}, "544DF305ef3ef012108D770B259720E7Ef6360Bd": {"balance": "0x2116545850052128000000"}, "42a7bD36593c75c981C7201d2E7974133782f0e0": {"balance": "0x0c9a0d0a69d549fff26def00"}, - "493044fbBAA7F9F78379864fA88aCcaFf6A7586e": {"balance": "0x5150ae84a8cdf00000"} + "493044fbBAA7F9F78379864fA88aCcaFf6A7586e": {"balance": "0x5150ae84a8cdf00000"} }, "number": "0x0", "gasUsed": "0x0", diff --git a/avalanchego/genesis/genesis_test.go b/avalanchego/genesis/genesis_test.go index 828f4b48..2482a876 100644 --- a/avalanchego/genesis/genesis_test.go +++ b/avalanchego/genesis/genesis_test.go @@ -397,9 +397,8 @@ func TestVMGenesis(t *testing.T) { expectedID: "7xKYhEvYuUekwDxozgEiMPufzJ3jJPypKbGE8ny6KL84z4RKB", }, { - vmID: constants.EVMID, - // expectedID: "2aamPVJ7SXz7DBUxFNY1Lyga52KWaVUH2Gz9Fetxo5gwjY5a29", - expectedID: "n5Q3QkMnpoq9h6nA36WrL5sH7fDNcyCBkMtP1r9He7AcQeR7y", + vmID: constants.EVMID, + expectedID: "erCt5pSo5d4bM8fMrsB2dRM54PGssDAVqRg1jHedQzr6ayLiq", }, }, }, From 58432abec53a4b728e9b05123979956c69eeabeb Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 13 May 2024 18:51:00 +0200 Subject: [PATCH 30/39] Updated Sgb transition timestamp for Coston --- coreth/params/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreth/params/config.go b/coreth/params/config.go index 361f3861..2350b550 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -148,7 +148,7 @@ var ( ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 14, 0, 0, 0, time.UTC).Unix()), ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 15, 0, 0, 0, time.UTC).Unix()), ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 16, 0, 0, 0, time.UTC).Unix()), - SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.April, 25, 0, 0, 0, 0, time.UTC).Unix()), + SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.May, 14, 12, 30, 0, 0, time.UTC).Unix()), } // LocalChainConfig is the configuration for the Songbird Local network. From 4249b78564627c04196dd790c688a6d22da8abc6 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 14 May 2024 10:20:53 +0200 Subject: [PATCH 31/39] Removed fork p-chain height for fuji network (=songbird) --- avalanchego/version/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avalanchego/version/constants.go b/avalanchego/version/constants.go index ca911101..b7e59fa9 100644 --- a/avalanchego/version/constants.go +++ b/avalanchego/version/constants.go @@ -92,7 +92,7 @@ var ( ApricotPhase4DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) ApricotPhase4MinPChainHeight = map[uint32]uint64{ constants.MainnetID: 793005, - constants.FujiID: 47437, + // constants.FujiID: 47437, } ApricotPhase4DefaultMinPChainHeight uint64 From 8fe191296541738581b67d1e1cf92024ab204a85 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Fri, 17 May 2024 13:34:44 +0200 Subject: [PATCH 32/39] Removed references to fuji network in avalanchego (songbird networkid clash) --- avalanchego/config/config.go | 6 +- avalanchego/genesis/beacons.go | 117 +----------------- avalanchego/genesis/genesis.go | 4 +- avalanchego/genesis/genesis_test.go | 21 ++-- avalanchego/utils/constants/network_ids.go | 30 ++--- .../utils/constants/network_ids_test.go | 8 +- 6 files changed, 31 insertions(+), 155 deletions(-) diff --git a/avalanchego/config/config.go b/avalanchego/config/config.go index 268dbff2..482909c2 100644 --- a/avalanchego/config/config.go +++ b/avalanchego/config/config.go @@ -729,7 +729,7 @@ func getStakingConfig(v *viper.Viper, networkID uint32) (node.StakingConfig, err return node.StakingConfig{}, errInvalidStakerWeights } - if !config.EnableStaking && (networkID == constants.MainnetID || networkID == constants.FujiID) { + if !config.EnableStaking && networkID == constants.MainnetID { return node.StakingConfig{}, errStakingDisableOnPublicNetwork } @@ -738,7 +738,7 @@ func getStakingConfig(v *viper.Viper, networkID uint32) (node.StakingConfig, err if err != nil { return node.StakingConfig{}, err } - if networkID != constants.MainnetID && networkID != constants.FujiID { + if networkID != constants.MainnetID { config.UptimeRequirement = v.GetFloat64(UptimeRequirementKey) config.MinValidatorStake = v.GetUint64(MinValidatorStakeKey) config.MaxValidatorStake = v.GetUint64(MaxValidatorStakeKey) @@ -775,7 +775,7 @@ func getStakingConfig(v *viper.Viper, networkID uint32) (node.StakingConfig, err } func getTxFeeConfig(v *viper.Viper, networkID uint32) genesis.TxFeeConfig { - if networkID != constants.MainnetID && networkID != constants.FujiID { + if networkID != constants.MainnetID { return genesis.TxFeeConfig{ TxFee: v.GetUint64(TxFeeKey), CreateAssetTxFee: v.GetUint64(CreateAssetTxFeeKey), diff --git a/avalanchego/genesis/beacons.go b/avalanchego/genesis/beacons.go index 0d642041..372746ed 100644 --- a/avalanchego/genesis/beacons.go +++ b/avalanchego/genesis/beacons.go @@ -4,126 +4,17 @@ package genesis import ( - "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/sampler" ) // getIPs returns the beacon IPs for each network -func getIPs(networkID uint32) []string { - switch networkID { - case constants.MainnetID: - return []string{ - "54.94.43.49:9651", - "52.79.47.77:9651", - "18.229.206.191:9651", - "3.34.221.73:9651", - "13.244.155.170:9651", - "13.244.47.224:9651", - "122.248.200.212:9651", - "52.30.9.211:9651", - "122.248.199.127:9651", - "18.202.190.40:9651", - "15.206.182.45:9651", - "15.207.11.193:9651", - "44.226.118.72:9651", - "54.185.87.50:9651", - "18.158.15.12:9651", - "3.21.38.33:9651", - "54.93.182.129:9651", - "3.128.138.36:9651", - "3.104.107.241:9651", - "3.106.25.139:9651", - "18.162.129.129:9651", - "18.162.161.230:9651", - "52.47.181.114:9651", - "15.188.9.42:9651", - } - case constants.FujiID: - return []string{ - "3.214.61.227:9651", - "52.206.218.4:9651", - "44.194.128.146:9651", - "3.143.146.90:9651", - "3.142.66.84:9651", - "3.142.32.15:9651", - "44.240.251.247:9651", - "44.224.22.217:9651", - "52.13.58.52:9651", - "18.163.142.196:9651", - "16.162.54.143:9651", - "18.167.153.71:9651", - "52.29.183.160:9651", - "18.159.63.226:9651", - "3.65.152.247:9651", - "34.247.100.96:9651", - "34.250.89.215:9651", - "54.228.143.65:9651", - "54.232.253.20:9651", - "54.94.159.80:9651", - "54.94.242.98:9651", - } - default: - return nil - } +func getIPs(_ uint32) []string { + return nil } // getNodeIDs returns the beacon node IDs for each network -func getNodeIDs(networkID uint32) []string { - switch networkID { - case constants.MainnetID: - return []string{ - "NodeID-A6onFGyJjA37EZ7kYHANMR1PFRT8NmXrF", - "NodeID-6SwnPJLH8cWfrJ162JjZekbmzaFpjPcf", - "NodeID-GSgaA47umS1px2ohVjodW9621Ks63xDxD", - "NodeID-BQEo5Fy1FRKLbX51ejqDd14cuSXJKArH2", - "NodeID-Drv1Qh7iJvW3zGBBeRnYfCzk56VCRM2GQ", - "NodeID-DAtCoXfLT6Y83dgJ7FmQg8eR53hz37J79", - "NodeID-FGRoKnyYKFWYFMb6Xbocf4hKuyCBENgWM", - "NodeID-Dw7tuwxpAmcpvVGp9JzaHAR3REPoJ8f2R", - "NodeID-4kCLS16Wy73nt1Zm54jFZsL7Msrv3UCeJ", - "NodeID-9T7NXBFpp8LWCyc58YdKNoowDipdVKAWz", - "NodeID-6ghBh6yof5ouMCya2n9fHzhpWouiZFVVj", - "NodeID-HiFv1DpKXkAAfJ1NHWVqQoojjznibZXHP", - "NodeID-Fv3t2shrpkmvLnvNzcv1rqRKbDAYFnUor", - "NodeID-AaxT2P4uuPAHb7vAD8mNvjQ3jgyaV7tu9", - "NodeID-kZNuQMHhydefgnwjYX1fhHMpRNAs9my1", - "NodeID-A7GwTSd47AcDVqpTVj7YtxtjHREM33EJw", - "NodeID-Hr78Fy8uDYiRYocRYHXp4eLCYeb8x5UuM", - "NodeID-9CkG9MBNavnw7EVSRsuFr7ws9gascDQy3", - "NodeID-A8jypu63CWp76STwKdqP6e9hjL675kdiG", - "NodeID-HsBEx3L71EHWSXaE6gvk2VsNntFEZsxqc", - "NodeID-Nr584bLpGgbCUbZFSBaBz3Xum5wpca9Ym", - "NodeID-QKGoUvqcgormCoMj6yPw9isY7DX9H4mdd", - "NodeID-HCw7S2TVbFPDWNBo1GnFWqJ47f9rDJtt1", - "NodeID-FYv1Lb29SqMpywYXH7yNkcFAzRF2jvm3K", - } - case constants.FujiID: - return []string{ - "NodeID-2m38qc95mhHXtrhjyGbe7r2NhniqHHJRB", - "NodeID-JjvzhxnLHLUQ5HjVRkvG827ivbLXPwA9u", - "NodeID-LegbVf6qaMKcsXPnLStkdc1JVktmmiDxy", - "NodeID-HGZ8ae74J3odT8ESreAdCtdnvWG1J4X5n", - "NodeID-CYKruAjwH1BmV3m37sXNuprbr7dGQuJwG", - "NodeID-4KXitMCoE9p2BHA6VzXtaTxLoEjNDo2Pt", - "NodeID-LQwRLm4cbJ7T2kxcxp4uXCU5XD8DFrE1C", - "NodeID-4CWTbdvgXHY1CLXqQNAp22nJDo5nAmts6", - "NodeID-4QBwET5o8kUhvt9xArhir4d3R25CtmZho", - "NodeID-JyE4P8f4cTryNV8DCz2M81bMtGhFFHexG", - "NodeID-EDESh4DfZFC15i613pMtWniQ9arbBZRnL", - "NodeID-BFa1padLXBj7VHa2JYvYGzcTBPQGjPhUy", - "NodeID-CZmZ9xpCzkWqjAyS7L4htzh5Lg6kf1k18", - "NodeID-FesGqwKq7z5nPFHa5iwZctHE5EZV9Lpdq", - "NodeID-84KbQHSDnojroCVY7vQ7u9Tx7pUonPaS", - "NodeID-CTtkcXvVdhpNp6f97LEUXPwsRD3A2ZHqP", - "NodeID-hArafGhY2HFTbwaaVh1CSCUCUCiJ2Vfb", - "NodeID-4B4rc5vdD1758JSBYL1xyvE5NHGzz6xzH", - "NodeID-EzGaipqomyK9UKx9DBHV6Ky3y68hoknrF", - "NodeID-NpagUxt6KQiwPch9Sd4osv8kD1TZnkjdk", - "NodeID-3VWnZNViBP2b56QBY7pNJSLzN2rkTyqnK", - } - default: - return nil - } +func getNodeIDs(_ uint32) []string { + return nil } // SampleBeacons returns the some beacons this node should connect to diff --git a/avalanchego/genesis/genesis.go b/avalanchego/genesis/genesis.go index 44958eca..d50f8bef 100644 --- a/avalanchego/genesis/genesis.go +++ b/avalanchego/genesis/genesis.go @@ -185,7 +185,7 @@ func validateConfig(networkID uint32, config *Config) error { // 2. The asset ID of AVAX func FromFile(networkID uint32, filepath string) ([]byte, ids.ID, error) { switch networkID { - case constants.MainnetID, constants.TestnetID, constants.LocalID: + case constants.FlareID, constants.SongbirdID, constants.CostwoID, constants.CostonID, constants.LocalFlareID, constants.LocalID: return nil, ids.ID{}, fmt.Errorf( "cannot override genesis config for standard network %s (%d)", constants.NetworkName(networkID), @@ -226,7 +226,7 @@ func FromFile(networkID uint32, filepath string) ([]byte, ids.ID, error) { // 2. The asset ID of AVAX func FromFlag(networkID uint32, genesisContent string) ([]byte, ids.ID, error) { switch networkID { - case constants.MainnetID, constants.TestnetID, constants.LocalID: + case constants.FlareID, constants.SongbirdID, constants.CostwoID, constants.CostonID, constants.LocalFlareID, constants.LocalID: return nil, ids.ID{}, fmt.Errorf( "cannot override genesis config for standard network %s (%d)", constants.NetworkName(networkID), diff --git a/avalanchego/genesis/genesis_test.go b/avalanchego/genesis/genesis_test.go index 2482a876..e2f2627b 100644 --- a/avalanchego/genesis/genesis_test.go +++ b/avalanchego/genesis/genesis_test.go @@ -167,9 +167,9 @@ func TestGenesisFromFile(t *testing.T) { expected string }{ "mainnet": { - networkID: constants.MainnetID, + networkID: constants.FlareID, customConfig: customGenesisConfigJSON, - err: "cannot override genesis config for standard network mainnet (1)", + err: "cannot override genesis config for standard network flare (14)", }, "songbird": { networkID: constants.SongbirdID, @@ -253,8 +253,8 @@ func TestGenesisFromFlag(t *testing.T) { expected string }{ "mainnet": { - networkID: constants.MainnetID, - err: "cannot override genesis config for standard network mainnet (1)", + networkID: constants.FlareID, + err: "cannot override genesis config for standard network flare (14)", }, "songbird": { networkID: constants.SongbirdID, @@ -303,8 +303,8 @@ func TestGenesisFromFlag(t *testing.T) { case constants.MainnetID: genBytes, err = json.Marshal(&MainnetConfig) require.NoError(err) - case constants.TestnetID: - genBytes, err = json.Marshal(&FujiConfig) + case constants.SongbirdID: + genBytes, err = json.Marshal(&SongbirdConfig) require.NoError(err) case constants.LocalID: genBytes, err = json.Marshal(&LocalConfig) @@ -340,13 +340,12 @@ func TestGenesis(t *testing.T) { expectedID string }{ { - networkID: constants.MainnetID, - expectedID: "UUvXi6j7QhVvgpbKM89MP5HdrxKm9CaJeHc187TsDNf8nZdLk", + networkID: constants.FlareID, + expectedID: "frq8jezXkuL4PmuBt6FDcpULh2sCsFHPgWq3ZGP1G8R8UnnoU", }, { - networkID: constants.SongbirdID, - // expectedID: "wehzwSstW6ChWVj356tLr6sJfSb8PaZMhcerXUyxsAUzia1Gr", - expectedID: "2r5gyF26aYN9BHaFYaDDFGpx2tiq4bz3GCiYUGo4QzKpzHeX9g", + networkID: constants.SongbirdID, + expectedID: "2ACyRqRc8H5VT7DDGn4qadKfct4iTPe9buQKhAjiDyotSVkeoi", }, { networkID: constants.LocalID, diff --git a/avalanchego/utils/constants/network_ids.go b/avalanchego/utils/constants/network_ids.go index b5bcac95..15612214 100644 --- a/avalanchego/utils/constants/network_ids.go +++ b/avalanchego/utils/constants/network_ids.go @@ -17,9 +17,7 @@ const ( CascadeID uint32 = 2 DenaliID uint32 = 3 EverestID uint32 = 4 - FujiID uint32 = 5 - TestnetID uint32 = FujiID UnitTestID uint32 = 10 LocalID uint32 = 12345 FlareID uint32 = 14 @@ -66,12 +64,10 @@ var ( PlatformChainID = ids.Empty NetworkIDToNetworkName = map[uint32]string{ - MainnetID: MainnetName, - CascadeID: CascadeName, - DenaliID: DenaliName, - EverestID: EverestName, - // SGB-MERGE - // FujiID: FujiName, + MainnetID: MainnetName, + CascadeID: CascadeName, + DenaliID: DenaliName, + EverestID: EverestName, UnitTestID: UnitTestName, LocalID: LocalName, FlareID: FlareName, @@ -86,17 +82,14 @@ var ( CascadeName: CascadeID, DenaliName: DenaliID, EverestName: EverestID, - FujiName: FujiID, - TestnetName: TestnetID, UnitTestName: UnitTestID, LocalName: LocalID, FlareName: FlareID, CostwoName: CostwoID, StagingName: StagingID, LocalFlareName: LocalFlareID, - // SGB-MERGE - SongbirdName: SongbirdID, - CostonName: CostonID, + SongbirdName: SongbirdID, + CostonName: CostonID, } NetworkIDToHRP = map[uint32]string{ @@ -110,25 +103,22 @@ var ( CostwoID: CostwoHRP, StagingID: StagingHRP, LocalFlareID: LocalFlareHRP, - // SGB-MERGE - SongbirdID: SongbirdHRP, - CostonID: CostonHRP, + SongbirdID: SongbirdHRP, + CostonID: CostonHRP, } NetworkHRPToNetworkID = map[string]uint32{ MainnetHRP: MainnetID, CascadeHRP: CascadeID, DenaliHRP: DenaliID, EverestHRP: EverestID, - FujiHRP: FujiID, UnitTestHRP: UnitTestID, LocalHRP: LocalID, FlareHRP: FlareID, CostwoHRP: CostwoID, StagingHRP: StagingID, LocalFlareHRP: LocalFlareID, - // SGB-MERGE - SongbirdHRP: SongbirdID, - CostonHRP: CostonID, + SongbirdHRP: SongbirdID, + CostonHRP: CostonID, } ValidNetworkPrefix = "network-" diff --git a/avalanchego/utils/constants/network_ids_test.go b/avalanchego/utils/constants/network_ids_test.go index e85fb779..e7b6c658 100644 --- a/avalanchego/utils/constants/network_ids_test.go +++ b/avalanchego/utils/constants/network_ids_test.go @@ -126,12 +126,8 @@ func TestNetworkID(t *testing.T) { id: MainnetID, }, { - name: TestnetName, - id: TestnetID, - }, - { - name: FujiName, - id: FujiID, + name: SongbirdName, + id: SongbirdID, }, { name: LocalName, From d1581b21fe8480ae43805e6b2cf89f64e8dee371 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Fri, 17 May 2024 13:40:44 +0200 Subject: [PATCH 33/39] Fixed typo in variable name --- avalanchego/snow/validators/custom.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/avalanchego/snow/validators/custom.go b/avalanchego/snow/validators/custom.go index c37c089b..6800ac98 100644 --- a/avalanchego/snow/validators/custom.go +++ b/avalanchego/snow/validators/custom.go @@ -37,12 +37,12 @@ func InitializeDefaultValidators(networkID uint32) { } type defaultValidatorSet struct { - initialzed bool - vdrMap map[ids.NodeID]Validator + initialized bool + vdrMap map[ids.NodeID]Validator } func (dvs *defaultValidatorSet) initialize(networkID uint32) { - if dvs.initialzed { + if dvs.initialized { return } @@ -59,11 +59,11 @@ func (dvs *defaultValidatorSet) initialize(networkID uint32) { for _, vdr := range vdrs { dvs.vdrMap[vdr.ID()] = vdr } - dvs.initialzed = true + dvs.initialized = true } func (dvs *defaultValidatorSet) list() []Validator { - if !dvs.initialzed { + if !dvs.initialized { panic(errNotInitialized) } vdrs := make([]Validator, 0, len(dvs.vdrMap)) @@ -74,7 +74,7 @@ func (dvs *defaultValidatorSet) list() []Validator { } func (dvs *defaultValidatorSet) isValidator(vdrID ids.NodeID) bool { - if !dvs.initialzed { + if !dvs.initialized { panic(errNotInitialized) } _, ok := dvs.vdrMap[vdrID] From a91cda78278feadaf67774c92afec0c082cbfd61 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Fri, 17 May 2024 13:49:30 +0200 Subject: [PATCH 34/39] Removed more references to fuji network in avalanchego (songbird networkid clash) --- avalanchego/genesis/config.go | 13 -- avalanchego/genesis/genesis_fuji.go | 43 ----- avalanchego/genesis/genesis_fuji.json | 185 --------------------- avalanchego/genesis/genesis_test.go | 13 -- avalanchego/genesis/params.go | 6 - avalanchego/utils/constants/network_ids.go | 2 - avalanchego/version/constants.go | 16 +- avalanchego/wallet/subnet/primary/api.go | 4 +- 8 files changed, 7 insertions(+), 275 deletions(-) delete mode 100644 avalanchego/genesis/genesis_fuji.go delete mode 100644 avalanchego/genesis/genesis_fuji.json diff --git a/avalanchego/genesis/config.go b/avalanchego/genesis/config.go index 0af45243..1738dbe5 100644 --- a/avalanchego/genesis/config.go +++ b/avalanchego/genesis/config.go @@ -152,10 +152,6 @@ var ( // genesis. MainnetConfig Config - // FujiConfig is the config that should be used to generate the fuji - // genesis. - FujiConfig Config - // LocalConfig is the config that should be used to generate a local // genesis. LocalConfig Config @@ -187,7 +183,6 @@ var ( func init() { unparsedMainnetConfig := UnparsedConfig{} - unparsedFujiConfig := UnparsedConfig{} unparsedLocalConfig := UnparsedConfig{} unparsedFlareConfig := UnparsedConfig{} unparsedCostwoConfig := UnparsedConfig{} @@ -199,7 +194,6 @@ func init() { errs := wrappers.Errs{} errs.Add( json.Unmarshal(mainnetGenesisConfigJSON, &unparsedMainnetConfig), - json.Unmarshal(fujiGenesisConfigJSON, &unparsedFujiConfig), json.Unmarshal([]byte(localGenesisConfigJSON), &unparsedLocalConfig), json.Unmarshal(flareGenesisConfigJSON, &unparsedFlareConfig), json.Unmarshal(costwoGenesisConfigJSON, &unparsedCostwoConfig), @@ -216,10 +210,6 @@ func init() { errs.Add(err) MainnetConfig = mainnetConfig - fujiConfig, err := unparsedFujiConfig.Parse() - errs.Add(err) - FujiConfig = fujiConfig - localConfig, err := unparsedLocalConfig.Parse() localConfig.CChainGenesis = localCChainGenesis errs.Add(err) @@ -260,9 +250,6 @@ func GetConfig(networkID uint32) *Config { switch networkID { case constants.MainnetID: return &MainnetConfig - // SGB-MERGE - // case constants.FujiID: - // return &FujiConfig case constants.LocalID: return &LocalConfig case constants.FlareID: diff --git a/avalanchego/genesis/genesis_fuji.go b/avalanchego/genesis/genesis_fuji.go deleted file mode 100644 index 65ac2cd2..00000000 --- a/avalanchego/genesis/genesis_fuji.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package genesis - -import ( - "time" - - _ "embed" - - "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/vms/platformvm/reward" -) - -var ( - //go:embed genesis_fuji.json - fujiGenesisConfigJSON []byte - - // FujiParams are the params used for the fuji testnet - FujiParams = Params{ - TxFeeConfig: TxFeeConfig{ - TxFee: units.MilliAvax, - CreateAssetTxFee: 10 * units.MilliAvax, - CreateSubnetTxFee: 100 * units.MilliAvax, - CreateBlockchainTxFee: 100 * units.MilliAvax, - }, - StakingConfig: StakingConfig{ - UptimeRequirement: .8, // 80% - MinValidatorStake: 1 * units.Avax, - MaxValidatorStake: 3 * units.MegaAvax, - MinDelegatorStake: 1 * units.Avax, - MinDelegationFee: 20000, // 2% - MinStakeDuration: 24 * time.Hour, - MaxStakeDuration: 365 * 24 * time.Hour, - RewardConfig: reward.Config{ - MaxConsumptionRate: .12 * reward.PercentDenominator, - MinConsumptionRate: .10 * reward.PercentDenominator, - MintingPeriod: 365 * 24 * time.Hour, - SupplyCap: 720 * units.MegaAvax, - }, - }, - } -) diff --git a/avalanchego/genesis/genesis_fuji.json b/avalanchego/genesis/genesis_fuji.json deleted file mode 100644 index c6d36407..00000000 --- a/avalanchego/genesis/genesis_fuji.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "networkID": 5, - "allocations": [ - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "initialAmount": 0, - "unlockSchedule": [ - { - "amount": 40000000000000000 - } - ] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji1xpmx0ljrpvqexrvrj26fnggvr0ax9wm32gaxmx", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji1wrv92qg5x3dsqrtukdc8qxnpqust3qdakxgm4s", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji1qrmj7u9pquyy3mahzxeq0nnlnj2aceedjfqqrq", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji1cap3ru2ghc3jtdnuyey738ru8u5ekdadcvrtyk", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji18g2m7483k6swe46cpfmq96t09sp63pgv7judr4", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji1zwe0kxhg73x3ehgtkkz24k9czlfgztc45hgrg3", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji1fqcs4m9p8gdp7gckk30n8u68d55jk0hdumx30f", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji18lany6fjlzxc7vuqfd9x4k9wqp0yhk074p283d", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji18lany6fjlzxc7vuqfd9x4k9wqp0yhk074p283d", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - }, - { - "ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d", - "avaxAddr": "X-fuji10d2fqjfl3ghl73z2ez65ufanxwwhccxugq8z2t", - "initialAmount": 32000000000000000, - "unlockSchedule": [] - } - ], - "startTime": 1599696000, - "initialStakeDuration": 31536000, - "initialStakeDurationOffset": 54000, - "initialStakedFunds": [ - "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" - ], - "initialStakers": [ - { - "nodeID": "NodeID-NpagUxt6KQiwPch9Sd4osv8kD1TZnkjdk", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 1000000 - }, - { - "nodeID": "NodeID-2m38qc95mhHXtrhjyGbe7r2NhniqHHJRB", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 500000 - }, - { - "nodeID": "NodeID-LQwRLm4cbJ7T2kxcxp4uXCU5XD8DFrE1C", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 250000 - }, - { - "nodeID": "NodeID-hArafGhY2HFTbwaaVh1CSCUCUCiJ2Vfb", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 125000 - }, - { - "nodeID": "NodeID-4QBwET5o8kUhvt9xArhir4d3R25CtmZho", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 62500 - }, - { - "nodeID": "NodeID-HGZ8ae74J3odT8ESreAdCtdnvWG1J4X5n", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 31250 - }, - { - "nodeID": "NodeID-4KXitMCoE9p2BHA6VzXtaTxLoEjNDo2Pt", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-JyE4P8f4cTryNV8DCz2M81bMtGhFFHexG", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-EzGaipqomyK9UKx9DBHV6Ky3y68hoknrF", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-CYKruAjwH1BmV3m37sXNuprbr7dGQuJwG", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-LegbVf6qaMKcsXPnLStkdc1JVktmmiDxy", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-FesGqwKq7z5nPFHa5iwZctHE5EZV9Lpdq", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-BFa1padLXBj7VHa2JYvYGzcTBPQGjPhUy", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-4B4rc5vdD1758JSBYL1xyvE5NHGzz6xzH", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-EDESh4DfZFC15i613pMtWniQ9arbBZRnL", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-CZmZ9xpCzkWqjAyS7L4htzh5Lg6kf1k18", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-CTtkcXvVdhpNp6f97LEUXPwsRD3A2ZHqP", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-84KbQHSDnojroCVY7vQ7u9Tx7pUonPaS", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-JjvzhxnLHLUQ5HjVRkvG827ivbLXPwA9u", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - }, - { - "nodeID": "NodeID-4CWTbdvgXHY1CLXqQNAp22nJDo5nAmts6", - "rewardAddress": "X-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw", - "delegationFee": 20000 - } - ], - "cChainGenesis": "{\"config\":{\"chainId\":43113,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}", - "message": "hi mom" -} \ No newline at end of file diff --git a/avalanchego/genesis/genesis_test.go b/avalanchego/genesis/genesis_test.go index e2f2627b..7dc8b6d6 100644 --- a/avalanchego/genesis/genesis_test.go +++ b/avalanchego/genesis/genesis_test.go @@ -39,10 +39,6 @@ func TestValidateConfig(t *testing.T) { networkID: 1, config: &MainnetConfig, }, - "fuji": { - networkID: 5, - config: &FujiConfig, - }, "local": { networkID: 162, config: &LocalFlareConfig, @@ -115,15 +111,6 @@ func TestValidateConfig(t *testing.T) { }(), err: "duplicated in initial staked funds", }, - "initial staked funds not in allocations": { - networkID: 5, - config: func() *Config { - thisConfig := FujiConfig - thisConfig.InitialStakedFunds = append(thisConfig.InitialStakedFunds, LocalFlareConfig.InitialStakedFunds[0]) - return &thisConfig - }(), - err: "does not have an allocation to stake", - }, "empty C-Chain genesis": { networkID: 162, config: func() *Config { diff --git a/avalanchego/genesis/params.go b/avalanchego/genesis/params.go index b16ac76a..fc80e003 100644 --- a/avalanchego/genesis/params.go +++ b/avalanchego/genesis/params.go @@ -53,9 +53,6 @@ func GetTxFeeConfig(networkID uint32) TxFeeConfig { switch networkID { case constants.MainnetID: return MainnetParams.TxFeeConfig - // SGB-MERGE - // case constants.FujiID: - // return FujiParams.TxFeeConfig case constants.LocalID: return LocalParams.TxFeeConfig case constants.FlareID: @@ -79,9 +76,6 @@ func GetStakingConfig(networkID uint32) StakingConfig { switch networkID { case constants.MainnetID: return MainnetParams.StakingConfig - // SGB-MERGE - // case constants.FujiID: - // return FujiParams.StakingConfig case constants.LocalID: return LocalParams.StakingConfig case constants.FlareID: diff --git a/avalanchego/utils/constants/network_ids.go b/avalanchego/utils/constants/network_ids.go index 15612214..04678b40 100644 --- a/avalanchego/utils/constants/network_ids.go +++ b/avalanchego/utils/constants/network_ids.go @@ -31,7 +31,6 @@ const ( CascadeName = "cascade" DenaliName = "denali" EverestName = "everest" - FujiName = "fuji" TestnetName = "testnet" UnitTestName = "testing" LocalName = "local" @@ -46,7 +45,6 @@ const ( CascadeHRP = "cascade" DenaliHRP = "denali" EverestHRP = "everest" - FujiHRP = "fuji" UnitTestHRP = "testing" LocalHRP = "local" FallbackHRP = "custom" diff --git a/avalanchego/version/constants.go b/avalanchego/version/constants.go index b7e59fa9..e2776d64 100644 --- a/avalanchego/version/constants.go +++ b/avalanchego/version/constants.go @@ -68,8 +68,7 @@ var ( } ApricotPhase3Times = map[uint32]time.Time{ - constants.MainnetID: time.Date(2021, time.August, 24, 14, 0, 0, 0, time.UTC), - // constants.FujiID: time.Date(2021, time.August, 16, 19, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(2021, time.August, 24, 14, 0, 0, 0, time.UTC), constants.FlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), @@ -80,8 +79,7 @@ var ( ApricotPhase3DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) ApricotPhase4Times = map[uint32]time.Time{ - constants.MainnetID: time.Date(2021, time.September, 22, 21, 0, 0, 0, time.UTC), - // constants.FujiID: time.Date(2021, time.September, 16, 21, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(2021, time.September, 22, 21, 0, 0, 0, time.UTC), constants.FlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), @@ -92,13 +90,11 @@ var ( ApricotPhase4DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) ApricotPhase4MinPChainHeight = map[uint32]uint64{ constants.MainnetID: 793005, - // constants.FujiID: 47437, } ApricotPhase4DefaultMinPChainHeight uint64 ApricotPhase5Times = map[uint32]time.Time{ - constants.MainnetID: time.Date(2021, time.December, 2, 18, 0, 0, 0, time.UTC), - // constants.FujiID: time.Date(2021, time.November, 24, 15, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(2021, time.December, 2, 18, 0, 0, 0, time.UTC), constants.FlareID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(2022, time.June, 1, 0, 0, 0, 0, time.UTC), @@ -110,8 +106,7 @@ var ( // FIXME: update this before release BlueberryTimes = map[uint32]time.Time{ - constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), - // constants.FujiID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.FlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), @@ -123,8 +118,7 @@ var ( // FIXME: update this before release XChainMigrationTimes = map[uint32]time.Time{ - constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), - // constants.FujiID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.FlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), diff --git a/avalanchego/wallet/subnet/primary/api.go b/avalanchego/wallet/subnet/primary/api.go index cb6db101..3049944e 100644 --- a/avalanchego/wallet/subnet/primary/api.go +++ b/avalanchego/wallet/subnet/primary/api.go @@ -21,14 +21,14 @@ import ( const ( MainnetAPIURI = "https://api.avax.network" - FujiAPIURI = "https://api.avax-test.network" LocalAPIURI = "http://localhost:9650" fetchLimit = 1024 ) // TODO: refactor UTXOClient definition to allow the client implementations to -// perform their own assertions. +// +// perform their own assertions. var ( _ UTXOClient = platformvm.Client(nil) _ UTXOClient = avm.Client(nil) From 63ed69928da4caed5451c7ee7c9947be33b69fa8 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Fri, 7 Jun 2024 14:41:44 +0200 Subject: [PATCH 35/39] Revert to the default gas limit after allowing transfer between chains --- coreth/consensus/dummy/consensus.go | 11 ++++++++--- coreth/core/chain_makers.go | 3 ++- coreth/miner/worker.go | 4 +++- coreth/params/avalanche_params.go | 2 ++ coreth/plugin/evm/block_verification.go | 21 +++++++++++++++------ 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/coreth/consensus/dummy/consensus.go b/coreth/consensus/dummy/consensus.go index a968a56a..38c8c054 100644 --- a/coreth/consensus/dummy/consensus.go +++ b/coreth/consensus/dummy/consensus.go @@ -97,10 +97,15 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) } if config.IsSongbirdCode() { - // SGB-MERGE // Verify that the gas limit is correct for the current phase - if config.IsApricotPhase5(timestamp) && header.GasLimit != params.SgbApricotPhase5GasLimit { - return fmt.Errorf("expected gas limit to be %d in apricot phase 5 but got %d", params.SgbApricotPhase5GasLimit, header.GasLimit) + if config.IsSongbirdTransition(timestamp) { + if header.GasLimit != params.SgbTransitionGasLimit { + return fmt.Errorf("expected gas limit to be %d in sgb transition but got %d", params.SgbTransitionGasLimit, header.GasLimit) + } + } else if config.IsApricotPhase5(timestamp) { + if header.GasLimit != params.SgbApricotPhase5GasLimit { + return fmt.Errorf("expected gas limit to be %d in apricot phase 5 but got %d", params.SgbApricotPhase5GasLimit, header.GasLimit) + } } } else { if config.IsApricotPhase1(timestamp) { diff --git a/coreth/core/chain_makers.go b/coreth/core/chain_makers.go index ee0bdffc..584ae441 100644 --- a/coreth/core/chain_makers.go +++ b/coreth/core/chain_makers.go @@ -291,7 +291,8 @@ func makeHeader(chain consensus.ChainReader, config *params.ChainConfig, parent timestamp := new(big.Int).SetUint64(time) var gasLimit uint64 if config.IsSongbirdCode() { - // SGB-MERGE + if config.IsSongbirdTransition(timestamp) { + gasLimit = params.SgbTransitionGasLimit if config.IsApricotPhase5(timestamp) { gasLimit = params.SgbApricotPhase5GasLimit } else if config.IsApricotPhase1(timestamp) { diff --git a/coreth/miner/worker.go b/coreth/miner/worker.go index 78cd9ab4..0d349031 100644 --- a/coreth/miner/worker.go +++ b/coreth/miner/worker.go @@ -129,7 +129,9 @@ func (w *worker) commitNewWork() (*types.Block, error) { var gasLimit uint64 if w.chainConfig.IsSongbirdCode() { - if w.chainConfig.IsApricotPhase5(big.NewInt(timestamp)) { + if w.chainConfig.IsSongbirdTransition(big.NewInt(timestamp)) { + gasLimit = params.SgbTransitionGasLimit + } else if w.chainConfig.IsApricotPhase5(big.NewInt(timestamp)) { gasLimit = params.SgbApricotPhase5GasLimit } else if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { gasLimit = params.ApricotPhase1GasLimit diff --git a/coreth/params/avalanche_params.go b/coreth/params/avalanche_params.go index d7b32aea..a1784960 100644 --- a/coreth/params/avalanche_params.go +++ b/coreth/params/avalanche_params.go @@ -21,6 +21,8 @@ const ( ApricotPhase1GasLimit uint64 = 8_000_000 // SGB-MERGE + // revert to the default gas limit after the transition due to re-enabled transfer between chains + SgbTransitionGasLimit uint64 = 8_000_000 SgbApricotPhase5GasLimit uint64 = 30_000_000 ApricotPhase3ExtraDataSize uint64 = 80 diff --git a/coreth/plugin/evm/block_verification.go b/coreth/plugin/evm/block_verification.go index 621d1e8d..856b4f82 100644 --- a/coreth/plugin/evm/block_verification.go +++ b/coreth/plugin/evm/block_verification.go @@ -100,12 +100,21 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { // Enforce static gas limit after ApricotPhase1 (prior to ApricotPhase1 it's handled in processing). if rules.IsSongbirdCode { - // SGB-MERGE - if rules.IsApricotPhase5 && ethHeader.GasLimit != params.SgbApricotPhase5GasLimit { - return fmt.Errorf( - "expected gas limit to be %d in apricot phase 5 but got %d", - params.SgbApricotPhase5GasLimit, ethHeader.GasLimit, - ) + if rules.IsSongbirdTransition { + if ethHeader.GasLimit != params.SgbTransitionGasLimit { + return fmt.Errorf( + "expected gas limit to be %d in sgb transition but got %d", + params.SgbTransitionGasLimit, ethHeader.GasLimit, + ) + } + } else if rules.IsApricotPhase5 { + if ethHeader.GasLimit != params.SgbApricotPhase5GasLimit { + return fmt.Errorf( + "expected gas limit to be %d in apricot phase 5 but got %d", + params.SgbApricotPhase5GasLimit, ethHeader.GasLimit, + ) + + } } } else { if rules.IsApricotPhase1 { From 7a3db361a9933d33244bd09a666e708fdee6cf91 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 18 Jun 2024 14:14:16 +0200 Subject: [PATCH 36/39] Fixed missing else --- coreth/core/chain_makers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreth/core/chain_makers.go b/coreth/core/chain_makers.go index 584ae441..c15b7798 100644 --- a/coreth/core/chain_makers.go +++ b/coreth/core/chain_makers.go @@ -293,7 +293,7 @@ func makeHeader(chain consensus.ChainReader, config *params.ChainConfig, parent if config.IsSongbirdCode() { if config.IsSongbirdTransition(timestamp) { gasLimit = params.SgbTransitionGasLimit - if config.IsApricotPhase5(timestamp) { + } else if config.IsApricotPhase5(timestamp) { gasLimit = params.SgbApricotPhase5GasLimit } else if config.IsApricotPhase1(timestamp) { gasLimit = params.ApricotPhase1GasLimit From 8c09bb8f860789e1e1eba97f1503fb3f375b8ab5 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Thu, 27 Jun 2024 14:49:54 +0200 Subject: [PATCH 37/39] Increased max validator stake for Coston network --- avalanchego/vms/platformvm/txs/executor/inflation_settings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go index 650c6080..f3ef03cf 100644 --- a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go +++ b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go @@ -180,7 +180,7 @@ func getCostonInflationSettings(currentTimestamp time.Time, config *config.Confi default: return InflationSettings{ MinValidatorStake: 100 * units.KiloAvax, - MaxValidatorStake: 50 * units.MegaAvax, + MaxValidatorStake: 500 * units.MegaAvax, MinDelegatorStake: 10 * units.KiloAvax, MinDelegationFee: 0, MinStakeDuration: 24 * time.Hour, From e5bdc76633511540b050b798823d98a032014782 Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Mon, 1 Jul 2024 17:16:50 +0200 Subject: [PATCH 38/39] Upgrade to Avalanche 1.9.0 --- avalanchego/.golangci.yml | 2 - avalanchego/LICENSE.header | 4 +- avalanchego/README.md | 13 +- avalanchego/RELEASES.md | 228 ++ avalanchego/api/admin/client.go | 2 +- avalanchego/api/admin/client_test.go | 2 +- avalanchego/api/admin/service.go | 2 +- avalanchego/api/admin/service_test.go | 2 +- avalanchego/api/auth/auth.go | 2 +- avalanchego/api/auth/auth_test.go | 2 +- avalanchego/api/auth/claims.go | 2 +- avalanchego/api/auth/response.go | 2 +- avalanchego/api/auth/service.go | 2 +- avalanchego/api/common_args_responses.go | 2 +- avalanchego/api/health/checker.go | 2 +- avalanchego/api/health/client.go | 2 +- avalanchego/api/health/client_test.go | 2 +- avalanchego/api/health/handler.go | 2 +- avalanchego/api/health/health.go | 2 +- avalanchego/api/health/health_test.go | 2 +- avalanchego/api/health/metrics.go | 2 +- avalanchego/api/health/result.go | 2 +- avalanchego/api/health/service.go | 2 +- avalanchego/api/health/service_test.go | 2 +- avalanchego/api/health/worker.go | 2 +- avalanchego/api/info/client.go | 9 +- avalanchego/api/info/service.go | 47 +- avalanchego/api/info/service_test.go | 2 +- avalanchego/api/ipcs/client.go | 2 +- avalanchego/api/ipcs/service.go | 2 +- .../api/keystore/blockchain_keystore.go | 2 +- avalanchego/api/keystore/client.go | 2 +- avalanchego/api/keystore/codec.go | 2 +- .../api/keystore/gkeystore/keystore_client.go | 2 +- .../api/keystore/gkeystore/keystore_server.go | 2 +- avalanchego/api/keystore/keystore.go | 2 +- avalanchego/api/keystore/service.go | 2 +- avalanchego/api/keystore/service_test.go | 2 +- avalanchego/api/metrics/gatherer_test.go | 2 +- avalanchego/api/metrics/multi_gatherer.go | 2 +- .../api/metrics/multi_gatherer_test.go | 2 +- avalanchego/api/metrics/optional_gatherer.go | 2 +- .../api/metrics/optional_gatherer_test.go | 2 +- avalanchego/api/server/middleware_handler.go | 2 +- avalanchego/api/server/router.go | 2 +- avalanchego/api/server/router_test.go | 2 +- avalanchego/api/server/server.go | 2 +- avalanchego/api/server/wrapper.go | 2 +- avalanchego/app/app.go | 2 +- avalanchego/app/plugin/plugin.go | 2 +- avalanchego/app/plugin/plugin_client.go | 2 +- avalanchego/app/plugin/plugin_server.go | 2 +- avalanchego/app/plugin/process.go | 2 +- avalanchego/app/process/process.go | 13 +- avalanchego/app/runner/config.go | 2 +- avalanchego/app/runner/runner.go | 2 +- avalanchego/cache/cache.go | 2 +- avalanchego/cache/lru_cache.go | 2 +- avalanchego/cache/lru_cache_benchmark_test.go | 2 +- avalanchego/cache/lru_cache_test.go | 2 +- avalanchego/cache/metercacher/cache.go | 2 +- avalanchego/cache/metercacher/cache_test.go | 2 +- avalanchego/cache/metercacher/metrics.go | 2 +- avalanchego/cache/test_cacher.go | 2 +- avalanchego/cache/unique_cache.go | 2 +- avalanchego/cache/unique_cache_test.go | 2 +- avalanchego/chains/atomic/codec.go | 2 +- .../atomic/gsharedmemory/filtered_batch.go | 2 +- .../gsharedmemory/shared_memory_client.go | 2 +- .../gsharedmemory/shared_memory_server.go | 2 +- .../gsharedmemory/shared_memory_test.go | 2 +- avalanchego/chains/atomic/memory.go | 2 +- avalanchego/chains/atomic/memory_test.go | 2 +- avalanchego/chains/atomic/prefixes.go | 2 +- avalanchego/chains/atomic/shared_memory.go | 2 +- .../chains/atomic/shared_memory_test.go | 2 +- avalanchego/chains/atomic/state.go | 2 +- .../chains/atomic/test_shared_memory.go | 2 +- avalanchego/chains/atomic/writer.go | 2 +- avalanchego/chains/manager.go | 29 +- avalanchego/chains/mock_manager.go | 2 +- avalanchego/chains/registrant.go | 2 +- avalanchego/chains/subnet.go | 2 +- avalanchego/chains/subnet_test.go | 2 +- avalanchego/codec/codec.go | 2 +- avalanchego/codec/general_codec.go | 2 +- avalanchego/codec/hierarchycodec/codec.go | 2 +- .../codec/hierarchycodec/codec_test.go | 2 +- avalanchego/codec/linearcodec/codec.go | 2 +- avalanchego/codec/linearcodec/codec_test.go | 2 +- avalanchego/codec/manager.go | 2 +- .../codec/reflectcodec/struct_fielder.go | 2 +- avalanchego/codec/reflectcodec/type_codec.go | 15 +- avalanchego/codec/registry.go | 2 +- avalanchego/codec/test_codec.go | 2 +- avalanchego/config/config.go | 108 +- avalanchego/config/config_test.go | 2 +- avalanchego/config/flags.go | 56 +- avalanchego/config/keys.go | 19 +- avalanchego/config/pflags.go | 2 +- avalanchego/config/viper.go | 2 +- avalanchego/database/batch.go | 2 +- avalanchego/database/benchmark_database.go | 2 +- avalanchego/database/common.go | 2 +- avalanchego/database/corruptabledb/db.go | 2 +- avalanchego/database/corruptabledb/db_test.go | 2 +- avalanchego/database/database.go | 2 +- avalanchego/database/encdb/db.go | 2 +- avalanchego/database/encdb/db_test.go | 2 +- avalanchego/database/errors.go | 2 +- avalanchego/database/helpers.go | 2 +- avalanchego/database/iterator.go | 2 +- avalanchego/database/leveldb/db.go | 2 +- avalanchego/database/leveldb/db_test.go | 2 +- avalanchego/database/leveldb/metrics.go | 2 +- avalanchego/database/linkeddb/codec.go | 2 +- avalanchego/database/linkeddb/linkeddb.go | 2 +- .../database/linkeddb/linkeddb_test.go | 2 +- avalanchego/database/manager/manager.go | 2 +- avalanchego/database/manager/manager_test.go | 2 +- .../database/manager/versioned_database.go | 2 +- avalanchego/database/memdb/db.go | 2 +- avalanchego/database/memdb/db_test.go | 2 +- avalanchego/database/meterdb/db.go | 2 +- avalanchego/database/meterdb/db_test.go | 2 +- avalanchego/database/meterdb/metrics.go | 2 +- avalanchego/database/mockdb/db.go | 2 +- avalanchego/database/mockdb/db_test.go | 2 +- avalanchego/database/nodb/db.go | 2 +- avalanchego/database/prefixdb/db.go | 2 +- avalanchego/database/prefixdb/db_test.go | 2 +- avalanchego/database/rpcdb/db_client.go | 2 +- avalanchego/database/rpcdb/db_server.go | 2 +- avalanchego/database/rpcdb/db_test.go | 2 +- avalanchego/database/rpcdb/errors.go | 2 +- avalanchego/database/test_database.go | 2 +- avalanchego/database/versiondb/db.go | 2 +- avalanchego/database/versiondb/db_test.go | 2 +- avalanchego/genesis/aliases.go | 2 +- avalanchego/genesis/beacons.go | 2 +- avalanchego/genesis/config.go | 2 +- avalanchego/genesis/genesis.go | 4 +- avalanchego/genesis/genesis_local.go | 15 +- avalanchego/genesis/genesis_localFlare.json | 4 +- avalanchego/genesis/genesis_mainnet.go | 15 +- avalanchego/genesis/genesis_test.go | 6 +- avalanchego/genesis/params.go | 12 +- avalanchego/genesis/unparsed_config.go | 2 +- avalanchego/go.mod | 13 +- avalanchego/go.sum | 36 +- avalanchego/ids/aliases.go | 2 +- avalanchego/ids/aliases_test.go | 2 +- avalanchego/ids/bag.go | 2 +- avalanchego/ids/bag_benchmark_test.go | 2 +- avalanchego/ids/bag_test.go | 2 +- avalanchego/ids/bit_set.go | 24 +- avalanchego/ids/bit_set_test.go | 16 +- avalanchego/ids/bits.go | 2 +- avalanchego/ids/bits_test.go | 2 +- .../ids/galiasreader/alias_reader_client.go | 2 +- .../ids/galiasreader/alias_reader_server.go | 2 +- .../ids/galiasreader/alias_reader_test.go | 2 +- avalanchego/ids/id.go | 2 +- avalanchego/ids/id_test.go | 2 +- avalanchego/ids/node_id.go | 2 +- avalanchego/ids/node_id_bag.go | 2 +- avalanchego/ids/node_id_set.go | 2 +- avalanchego/ids/node_id_test.go | 2 +- avalanchego/ids/queue.go | 49 - avalanchego/ids/queue_test.go | 73 - avalanchego/ids/set.go | 2 +- avalanchego/ids/set_benchmark_test.go | 2 +- avalanchego/ids/set_test.go | 2 +- avalanchego/ids/short.go | 2 +- avalanchego/ids/short_set.go | 2 +- avalanchego/ids/short_set_test.go | 2 +- avalanchego/ids/short_test.go | 2 +- avalanchego/ids/slice.go | 2 +- avalanchego/ids/test_aliases.go | 2 +- avalanchego/ids/test_generator.go | 2 +- avalanchego/ids/unique_bag.go | 14 +- avalanchego/ids/unique_bag_test.go | 6 +- avalanchego/indexer/client.go | 2 +- avalanchego/indexer/client_test.go | 2 +- avalanchego/indexer/container.go | 2 +- avalanchego/indexer/index.go | 2 +- avalanchego/indexer/index_test.go | 2 +- avalanchego/indexer/indexer.go | 2 +- avalanchego/indexer/indexer_test.go | 2 +- avalanchego/indexer/service.go | 2 +- avalanchego/ipcs/chainipc.go | 2 +- avalanchego/ipcs/eventsocket.go | 2 +- avalanchego/ipcs/socket/socket.go | 2 +- avalanchego/ipcs/socket/socket_test.go | 2 +- avalanchego/ipcs/socket/socket_unix.go | 2 +- avalanchego/ipcs/socket/socket_windows.go | 2 +- avalanchego/main/main.go | 2 +- avalanchego/message/builder_test.go | 283 +- avalanchego/message/codec.go | 39 +- avalanchego/message/codec_test.go | 77 +- avalanchego/message/creator.go | 22 +- avalanchego/message/fields.go | 2 +- avalanchego/message/inbound_msg_builder.go | 225 +- .../message/inbound_msg_builder_proto.go | 439 +++ .../message/inbound_msg_builder_proto_test.go | 35 + avalanchego/message/internal_msg_builder.go | 50 +- avalanchego/message/messages.go | 647 +++- .../message/messages_benchmark_test.go | 207 ++ avalanchego/message/messages_test.go | 1152 +++++++ avalanchego/message/ops.go | 34 +- avalanchego/message/outbound_msg_builder.go | 135 +- .../message/outbound_msg_builder_proto.go | 517 ++++ .../outbound_msg_builder_proto_test.go | 31 + avalanchego/message/test_message.go | 27 + avalanchego/nat/nat.go | 2 +- avalanchego/nat/no_router.go | 2 +- avalanchego/nat/pmp.go | 2 +- avalanchego/nat/upnp.go | 2 +- avalanchego/network/certs_test.go | 4 +- avalanchego/network/config.go | 4 +- avalanchego/network/conn_test.go | 2 +- avalanchego/network/dialer/dialer.go | 2 +- avalanchego/network/dialer/dialer_test.go | 2 +- avalanchego/network/dialer_test.go | 2 +- avalanchego/network/handler_test.go | 2 +- avalanchego/network/ip_signer.go | 2 +- avalanchego/network/ip_signer_test.go | 2 +- avalanchego/network/listener_test.go | 2 +- avalanchego/network/metrics.go | 13 +- avalanchego/network/network.go | 51 +- avalanchego/network/network_test.go | 94 +- avalanchego/network/peer/config.go | 27 +- avalanchego/network/peer/example_test.go | 2 +- avalanchego/network/peer/info.go | 2 +- avalanchego/network/peer/ip.go | 2 +- avalanchego/network/peer/message_queue.go | 28 +- .../network/peer/message_queue_test.go | 72 +- avalanchego/network/peer/metrics.go | 2 +- avalanchego/network/peer/msg_length.go | 2 +- avalanchego/network/peer/network.go | 2 +- avalanchego/network/peer/peer.go | 211 +- avalanchego/network/peer/peer_test.go | 174 +- avalanchego/network/peer/set.go | 4 +- avalanchego/network/peer/set_test.go | 2 +- avalanchego/network/peer/test_network.go | 2 +- avalanchego/network/peer/test_peer.go | 44 +- avalanchego/network/peer/tls_config.go | 15 +- avalanchego/network/peer/upgrader.go | 2 +- .../network/throttling/bandwidth_throttler.go | 2 +- .../throttling/bandwidth_throttler_test.go | 2 +- avalanchego/network/throttling/common.go | 2 +- .../network/throttling/dial_throttler.go | 2 +- .../network/throttling/dial_throttler_test.go | 2 +- .../throttling/inbound_conn_throttler.go | 2 +- .../throttling/inbound_conn_throttler_test.go | 2 +- .../inbound_conn_upgrade_throttler.go | 2 +- .../inbound_conn_upgrade_throttler_test.go | 2 +- .../inbound_msg_buffer_throttler.go | 2 +- .../inbound_msg_buffer_throttler_test.go | 2 +- .../throttling/inbound_msg_byte_throttler.go | 11 +- .../inbound_msg_byte_throttler_test.go | 4 +- .../throttling/inbound_msg_throttler.go | 2 +- .../throttling/no_inbound_msg_throttler.go | 2 +- .../throttling/outbound_msg_throttler.go | 2 +- .../throttling/outbound_msg_throttler_test.go | 2 +- .../network/throttling/release_func.go | 2 +- avalanchego/network/tracked_ip.go | 2 +- avalanchego/network/tracked_ip_test.go | 2 +- avalanchego/node/config.go | 13 +- avalanchego/node/node.go | 130 +- avalanchego/proto/Dockerfile.buf | 2 +- avalanchego/proto/README.md | 14 +- .../proto/aliasreader/aliasreader.proto | 12 +- avalanchego/proto/appsender/appsender.proto | 14 +- avalanchego/proto/buf.md | 1 + avalanchego/proto/buf.yaml | 2 +- avalanchego/proto/http/http.proto | 20 +- .../http/responsewriter/responsewriter.proto | 16 +- avalanchego/proto/keystore/keystore.proto | 8 +- avalanchego/proto/messenger/messenger.proto | 8 +- avalanchego/proto/p2p/p2p.proto | 291 ++ avalanchego/proto/pb/http/http.pb.go | 175 +- .../http/responsewriter/responsewriter.pb.go | 72 +- avalanchego/proto/pb/p2p/p2p.pb.go | 2657 +++++++++++++++++ avalanchego/proto/pb/vm/vm.pb.go | 707 +++-- avalanchego/proto/plugin/plugin.proto | 10 +- avalanchego/proto/rpcdb/rpcdb.proto | 33 +- .../proto/sharedmemory/sharedmemory.proto | 12 +- .../proto/subnetlookup/subnetlookup.proto | 8 +- avalanchego/proto/vm/vm.proto | 22 +- avalanchego/pubsub/connection.go | 2 +- avalanchego/pubsub/connections.go | 2 +- avalanchego/pubsub/filter_param.go | 2 +- avalanchego/pubsub/filter_test.go | 2 +- avalanchego/pubsub/filterer.go | 2 +- avalanchego/pubsub/messages.go | 2 +- avalanchego/pubsub/server.go | 2 +- avalanchego/scripts/build_test.sh | 7 + avalanchego/scripts/constants.sh | 6 + avalanchego/scripts/lint.sh | 2 +- avalanchego/scripts/protobuf_codegen.sh | 30 +- avalanchego/scripts/tests.e2e.sh | 35 +- avalanchego/scripts/versions.sh | 4 +- avalanchego/snow/acceptor.go | 2 +- avalanchego/snow/choices/decidable.go | 2 +- avalanchego/snow/choices/status.go | 2 +- avalanchego/snow/choices/status_test.go | 2 +- avalanchego/snow/choices/test_decidable.go | 2 +- .../snow/consensus/avalanche/consensus.go | 2 +- .../consensus/avalanche/consensus_test.go | 2 +- .../snow/consensus/avalanche/factory.go | 2 +- .../snow/consensus/avalanche/parameters.go | 2 +- .../consensus/avalanche/parameters_test.go | 2 +- .../avalanche/poll/early_term_no_traversal.go | 4 +- .../poll/early_term_no_traversal_test.go | 2 +- .../consensus/avalanche/poll/interfaces.go | 2 +- .../consensus/avalanche/poll/no_early_term.go | 2 +- .../avalanche/poll/no_early_term_test.go | 2 +- .../snow/consensus/avalanche/poll/set.go | 11 +- .../snow/consensus/avalanche/poll/set_test.go | 2 +- .../snow/consensus/avalanche/test_vertex.go | 2 +- .../snow/consensus/avalanche/topological.go | 8 +- .../consensus/avalanche/topological_test.go | 2 +- .../consensus/avalanche/transaction_vertex.go | 2 +- .../snow/consensus/avalanche/vertex.go | 2 +- avalanchego/snow/consensus/metrics/height.go | 2 +- avalanchego/snow/consensus/metrics/latency.go | 19 +- avalanchego/snow/consensus/metrics/polls.go | 2 +- .../snow/consensus/snowball/binary_slush.go | 2 +- .../consensus/snowball/binary_snowball.go | 2 +- .../snowball/binary_snowball_test.go | 2 +- .../consensus/snowball/binary_snowflake.go | 2 +- .../snowball/binary_snowflake_test.go | 2 +- .../snow/consensus/snowball/consensus.go | 2 +- .../snowball/consensus_performance_test.go | 2 +- .../snowball/consensus_reversibility_test.go | 2 +- .../snow/consensus/snowball/consensus_test.go | 2 +- .../snow/consensus/snowball/factory.go | 2 +- avalanchego/snow/consensus/snowball/flat.go | 2 +- .../snow/consensus/snowball/flat_test.go | 2 +- .../snow/consensus/snowball/network_test.go | 2 +- .../snow/consensus/snowball/nnary_slush.go | 2 +- .../snow/consensus/snowball/nnary_snowball.go | 2 +- .../consensus/snowball/nnary_snowball_test.go | 2 +- .../consensus/snowball/nnary_snowflake.go | 2 +- .../snowball/nnary_snowflake_test.go | 2 +- .../snow/consensus/snowball/parameters.go | 2 +- .../consensus/snowball/parameters_test.go | 2 +- avalanchego/snow/consensus/snowball/tree.go | 2 +- .../snow/consensus/snowball/tree_test.go | 9 +- .../snow/consensus/snowball/unary_snowball.go | 2 +- .../consensus/snowball/unary_snowball_test.go | 2 +- .../consensus/snowball/unary_snowflake.go | 2 +- .../snowball/unary_snowflake_test.go | 2 +- avalanchego/snow/consensus/snowman/block.go | 2 +- .../snow/consensus/snowman/consensus.go | 2 +- .../snow/consensus/snowman/consensus_test.go | 2 +- avalanchego/snow/consensus/snowman/factory.go | 2 +- .../snow/consensus/snowman/mock_block.go | 163 + .../snow/consensus/snowman/network_test.go | 2 +- .../snow/consensus/snowman/oracle_block.go | 2 +- .../snowman/poll/early_term_no_traversal.go | 2 +- .../poll/early_term_no_traversal_test.go | 2 +- .../snow/consensus/snowman/poll/interfaces.go | 2 +- .../consensus/snowman/poll/no_early_term.go | 2 +- .../snowman/poll/no_early_term_test.go | 2 +- .../snow/consensus/snowman/poll/set.go | 16 +- .../snow/consensus/snowman/poll/set_test.go | 2 +- .../snow/consensus/snowman/snowman_block.go | 2 +- .../snow/consensus/snowman/test_block.go | 2 +- .../snow/consensus/snowman/topological.go | 2 +- .../consensus/snowman/topological_test.go | 2 +- .../snow/consensus/snowstorm/acceptor.go | 2 +- .../consensus/snowstorm/benchmark_test.go | 2 +- .../snow/consensus/snowstorm/consensus.go | 2 +- .../consensus/snowstorm/consensus_test.go | 2 +- .../snow/consensus/snowstorm/directed.go | 2 +- .../snow/consensus/snowstorm/directed_test.go | 2 +- .../snow/consensus/snowstorm/factory.go | 2 +- .../snow/consensus/snowstorm/network_test.go | 2 +- .../snow/consensus/snowstorm/rejector.go | 2 +- .../snow/consensus/snowstorm/snowball.go | 2 +- .../snow/consensus/snowstorm/stringer.go | 2 +- .../snow/consensus/snowstorm/test_tx.go | 2 +- avalanchego/snow/consensus/snowstorm/tx.go | 2 +- avalanchego/snow/context.go | 2 +- .../avalanche/bootstrap/bootstrapper.go | 2 +- .../avalanche/bootstrap/bootstrapper_test.go | 2 +- .../snow/engine/avalanche/bootstrap/config.go | 2 +- .../engine/avalanche/bootstrap/metrics.go | 2 +- .../snow/engine/avalanche/bootstrap/tx_job.go | 2 +- .../engine/avalanche/bootstrap/vertex_job.go | 2 +- avalanchego/snow/engine/avalanche/config.go | 2 +- .../snow/engine/avalanche/config_test.go | 2 +- avalanchego/snow/engine/avalanche/engine.go | 2 +- .../snow/engine/avalanche/getter/getter.go | 4 +- .../engine/avalanche/getter/getter_test.go | 2 +- avalanchego/snow/engine/avalanche/issuer.go | 2 +- avalanchego/snow/engine/avalanche/metrics.go | 2 +- .../snow/engine/avalanche/mocks/engine.go | 14 - .../engine/avalanche/state/prefixed_state.go | 2 +- .../snow/engine/avalanche/state/serializer.go | 2 +- .../snow/engine/avalanche/state/state.go | 6 +- .../engine/avalanche/state/unique_vertex.go | 2 +- .../avalanche/state/unique_vertex_test.go | 2 +- .../engine/avalanche/test_avalanche_engine.go | 2 +- .../snow/engine/avalanche/transitive.go | 28 +- .../snow/engine/avalanche/transitive_test.go | 116 +- .../snow/engine/avalanche/vertex/builder.go | 2 +- .../engine/avalanche/vertex/builder_test.go | 2 +- .../snow/engine/avalanche/vertex/codec.go | 2 +- .../snow/engine/avalanche/vertex/heap.go | 2 +- .../snow/engine/avalanche/vertex/heap_test.go | 2 +- .../snow/engine/avalanche/vertex/manager.go | 2 +- .../snow/engine/avalanche/vertex/parser.go | 2 +- .../engine/avalanche/vertex/parser_test.go | 2 +- .../snow/engine/avalanche/vertex/sorting.go | 2 +- .../avalanche/vertex/stateless_vertex.go | 2 +- .../avalanche/vertex/stateless_vertex_test.go | 2 +- .../snow/engine/avalanche/vertex/storage.go | 2 +- .../engine/avalanche/vertex/test_builder.go | 2 +- .../engine/avalanche/vertex/test_manager.go | 2 +- .../engine/avalanche/vertex/test_parser.go | 2 +- .../engine/avalanche/vertex/test_storage.go | 2 +- .../snow/engine/avalanche/vertex/test_vm.go | 2 +- .../snow/engine/avalanche/vertex/vm.go | 2 +- avalanchego/snow/engine/avalanche/voter.go | 2 +- .../snow/engine/avalanche/voter_test.go | 2 +- .../common/appsender/appsender_client.go | 2 +- .../common/appsender/appsender_server.go | 2 +- .../snow/engine/common/bootstrapable.go | 2 +- .../snow/engine/common/bootstrapper.go | 2 +- avalanchego/snow/engine/common/config.go | 2 +- avalanchego/snow/engine/common/engine.go | 13 +- avalanchego/snow/engine/common/fetcher.go | 2 +- avalanchego/snow/engine/common/fx.go | 2 +- avalanchego/snow/engine/common/halter.go | 2 +- .../snow/engine/common/http_handler.go | 2 +- avalanchego/snow/engine/common/message.go | 2 +- avalanchego/snow/engine/common/mixed_query.go | 2 +- .../snow/engine/common/mixed_query_test.go | 5 - avalanchego/snow/engine/common/mock_sender.go | 72 +- .../snow/engine/common/no_ops_handlers.go | 12 +- avalanchego/snow/engine/common/queue/job.go | 2 +- avalanchego/snow/engine/common/queue/jobs.go | 6 +- .../snow/engine/common/queue/jobs_test.go | 2 +- .../snow/engine/common/queue/parser.go | 2 +- avalanchego/snow/engine/common/queue/state.go | 2 +- .../snow/engine/common/queue/test_job.go | 2 +- .../snow/engine/common/queue/test_parser.go | 2 +- avalanchego/snow/engine/common/requests.go | 13 +- .../snow/engine/common/requests_test.go | 2 +- avalanchego/snow/engine/common/sender.go | 28 +- .../snow/engine/common/state_syncer.go | 2 +- avalanchego/snow/engine/common/subnet.go | 2 +- .../snow/engine/common/subnet_tracker.go | 4 +- .../snow/engine/common/test_bootstrapable.go | 2 +- .../snow/engine/common/test_bootstrapper.go | 2 +- avalanchego/snow/engine/common/test_config.go | 2 +- avalanchego/snow/engine/common/test_engine.go | 18 +- avalanchego/snow/engine/common/test_sender.go | 34 +- avalanchego/snow/engine/common/test_subnet.go | 2 +- avalanchego/snow/engine/common/test_timer.go | 2 +- avalanchego/snow/engine/common/test_vm.go | 2 +- avalanchego/snow/engine/common/timer.go | 2 +- .../snow/engine/common/tracker/peers.go | 2 +- .../snow/engine/common/tracker/startup.go | 2 +- avalanchego/snow/engine/common/vm.go | 2 +- .../snow/engine/snowman/ancestor_tree.go | 2 +- .../snow/engine/snowman/ancestor_tree_test.go | 2 +- .../snow/engine/snowman/block/batched_vm.go | 2 +- .../engine/snowman/block/batched_vm_test.go | 2 +- .../engine/snowman/block/height_indexed_vm.go | 11 +- .../engine/snowman/block/state_summary.go | 2 +- .../engine/snowman/block/state_syncable_vm.go | 2 +- .../engine/snowman/block/test_batched_vm.go | 2 +- .../snowman/block/test_height_indexed_vm.go | 2 +- .../snowman/block/test_state_summary.go | 2 +- .../snowman/block/test_state_syncable_vm.go | 2 +- .../snow/engine/snowman/block/test_vm.go | 2 +- avalanchego/snow/engine/snowman/block/vm.go | 2 +- .../engine/snowman/bootstrap/block_job.go | 2 +- .../engine/snowman/bootstrap/bootstrapper.go | 2 +- .../snowman/bootstrap/bootstrapper_test.go | 2 +- .../snow/engine/snowman/bootstrap/config.go | 2 +- .../snow/engine/snowman/bootstrap/metrics.go | 2 +- avalanchego/snow/engine/snowman/config.go | 2 +- .../snow/engine/snowman/config_test.go | 2 +- avalanchego/snow/engine/snowman/engine.go | 2 +- .../snow/engine/snowman/getter/getter.go | 4 +- .../snow/engine/snowman/getter/getter_test.go | 2 +- avalanchego/snow/engine/snowman/issuer.go | 2 +- .../snow/engine/snowman/memory_block.go | 2 +- avalanchego/snow/engine/snowman/metrics.go | 2 +- .../snow/engine/snowman/mocks/engine.go | 14 - .../snow/engine/snowman/syncer/config.go | 2 +- .../engine/snowman/syncer/state_syncer.go | 2 +- .../snowman/syncer/state_syncer_test.go | 2 +- .../snow/engine/snowman/syncer/utils_test.go | 2 +- .../engine/snowman/test_snowman_engine.go | 2 +- avalanchego/snow/engine/snowman/transitive.go | 136 +- .../snow/engine/snowman/transitive_test.go | 232 +- avalanchego/snow/engine/snowman/voter.go | 2 +- avalanchego/snow/events/blockable.go | 2 +- avalanchego/snow/events/blocker.go | 2 +- avalanchego/snow/events/blocker_test.go | 2 +- .../snow/networking/benchlist/benchable.go | 2 +- .../snow/networking/benchlist/benchlist.go | 2 +- .../networking/benchlist/benchlist_test.go | 2 +- .../snow/networking/benchlist/manager.go | 2 +- .../snow/networking/benchlist/metrics.go | 2 +- .../networking/benchlist/test_benchable.go | 2 +- .../snow/networking/handler/handler.go | 511 +++- .../snow/networking/handler/handler_test.go | 14 +- .../snow/networking/handler/message_queue.go | 2 +- .../handler/message_queue_metrics.go | 2 +- .../networking/handler/message_queue_test.go | 258 +- .../snow/networking/handler/metrics.go | 2 +- avalanchego/snow/networking/handler/parser.go | 18 +- .../snow/networking/router/chain_router.go | 66 +- .../networking/router/chain_router_metrics.go | 2 +- .../networking/router/chain_router_test.go | 42 +- avalanchego/snow/networking/router/health.go | 2 +- .../snow/networking/router/inbound_handler.go | 2 +- avalanchego/snow/networking/router/router.go | 4 +- .../snow/networking/sender/external_sender.go | 2 +- avalanchego/snow/networking/sender/sender.go | 356 ++- .../snow/networking/sender/sender_test.go | 40 +- .../networking/sender/test_external_sender.go | 2 +- .../snow/networking/timeout/manager.go | 2 +- .../snow/networking/timeout/manager_test.go | 2 +- .../snow/networking/timeout/metrics.go | 2 +- .../networking/tracker/resource_tracker.go | 19 +- .../tracker/resource_tracker_test.go | 2 +- .../snow/networking/tracker/targeter_test.go | 2 +- avalanchego/snow/networking/worker/pool.go | 2 +- avalanchego/snow/state.go | 2 +- avalanchego/snow/uptime/locked_calculator.go | 2 +- .../snow/uptime/locked_calculator_test.go | 2 +- avalanchego/snow/uptime/manager.go | 34 +- avalanchego/snow/uptime/manager_test.go | 2 +- avalanchego/snow/uptime/state.go | 2 +- avalanchego/snow/uptime/test_state.go | 2 +- avalanchego/snow/validators/connector.go | 2 +- avalanchego/snow/validators/custom.go | 69 +- avalanchego/snow/validators/custom_test.go | 32 + avalanchego/snow/validators/manager.go | 2 +- avalanchego/snow/validators/set.go | 3 +- avalanchego/snow/validators/set_test.go | 37 +- avalanchego/snow/validators/state.go | 2 +- avalanchego/snow/validators/test_state.go | 2 +- avalanchego/snow/validators/validator.go | 2 +- avalanchego/staking/tls.go | 2 +- avalanchego/staking/tls_test.go | 2 +- avalanchego/tests/e2e/banff/suites.go | 147 + avalanchego/tests/e2e/blueberry/suites.go | 138 - avalanchego/tests/e2e/describe.go | 6 - avalanchego/tests/e2e/e2e.go | 338 ++- avalanchego/tests/e2e/e2e_test.go | 129 +- .../tests/e2e/p/permissionless_subnets.go | 249 ++ avalanchego/tests/e2e/p/workflow.go | 235 ++ avalanchego/tests/e2e/ping/suites.go | 31 +- .../tests/e2e/static-handlers/suites.go | 161 +- avalanchego/tests/e2e/whitelist-vtx/suites.go | 280 -- avalanchego/tests/e2e/x/transfer/virtuous.go | 374 +-- .../tests/e2e/x/whitelist-vtx/suites.go | 284 ++ avalanchego/tests/upgrade/upgrade_test.go | 2 +- avalanchego/utils/atomic_bool.go | 2 +- avalanchego/utils/atomic_interface.go | 2 +- avalanchego/utils/atomic_interface_test.go | 2 +- avalanchego/utils/beacon/beacon.go | 2 +- avalanchego/utils/beacon/set.go | 2 +- avalanchego/utils/beacon/set_test.go | 2 +- avalanchego/utils/bloom/bloom_filter.go | 2 +- avalanchego/utils/bloom/bloom_filter_test.go | 2 +- avalanchego/utils/bloom/map_filter.go | 2 +- avalanchego/utils/buffer/unbounded_queue.go | 103 + .../utils/buffer/unbounded_queue_test.go | 306 ++ avalanchego/utils/bytes.go | 2 +- avalanchego/utils/bytes_test.go | 2 +- avalanchego/utils/cb58/cb58.go | 2 +- avalanchego/utils/cb58/cb58_test.go | 2 +- avalanchego/utils/compression/compressor.go | 2 +- .../utils/compression/gzip_compressor.go | 22 +- .../utils/compression/gzip_compressor_test.go | 25 +- .../utils/compression/no_compressor.go | 2 +- .../utils/compression/no_compressor_test.go | 2 +- avalanchego/utils/constants/aliases.go | 2 +- avalanchego/utils/constants/application.go | 2 +- avalanchego/utils/constants/network_ids.go | 11 +- .../utils/constants/network_ids_test.go | 2 +- avalanchego/utils/constants/networking.go | 2 +- avalanchego/utils/crypto/bls/bls_test.go | 490 +++ avalanchego/utils/crypto/bls/public.go | 72 + avalanchego/utils/crypto/bls/public_test.go | 56 + avalanchego/utils/crypto/bls/secret.go | 64 + avalanchego/utils/crypto/bls/secret_test.go | 49 + avalanchego/utils/crypto/bls/signature.go | 57 + .../utils/crypto/bls/signature_test.go | 51 + avalanchego/utils/crypto/crypto.go | 2 +- .../utils/crypto/crypto_benchmark_test.go | 2 +- avalanchego/utils/crypto/ed25519.go | 2 +- avalanchego/utils/crypto/errors.go | 2 +- avalanchego/utils/crypto/rsa.go | 2 +- avalanchego/utils/crypto/rsapss.go | 2 +- avalanchego/utils/crypto/secp256k1r.go | 2 +- avalanchego/utils/crypto/secp256k1r_test.go | 2 +- avalanchego/utils/crypto/test_keys.go | 2 +- avalanchego/utils/filesystem/io.go | 2 +- avalanchego/utils/filesystem/mock_file.go | 2 +- .../utils/formatting/address/address.go | 2 +- .../utils/formatting/address/converter.go | 2 +- avalanchego/utils/formatting/address/tools.go | 2 +- avalanchego/utils/formatting/encoding.go | 2 +- .../formatting/encoding_benchmark_test.go | 2 +- avalanchego/utils/formatting/encoding_test.go | 2 +- avalanchego/utils/formatting/int_format.go | 2 +- .../utils/formatting/int_format_test.go | 2 +- .../utils/formatting/prefixed_stringer.go | 2 +- .../utils/hashing/consistent/hashable.go | 2 +- avalanchego/utils/hashing/consistent/ring.go | 2 +- .../utils/hashing/consistent/ring_test.go | 2 +- avalanchego/utils/hashing/hasher.go | 2 +- avalanchego/utils/hashing/hashing.go | 2 +- avalanchego/utils/ips/claimed_ip_port.go | 2 +- avalanchego/utils/ips/dynamic_ip_port.go | 2 +- avalanchego/utils/ips/ip_port.go | 2 +- avalanchego/utils/json/codec.go | 2 +- avalanchego/utils/json/float32.go | 2 +- avalanchego/utils/json/float32_test.go | 2 +- avalanchego/utils/json/float64.go | 2 +- avalanchego/utils/json/uint16.go | 2 +- avalanchego/utils/json/uint32.go | 2 +- avalanchego/utils/json/uint64.go | 2 +- avalanchego/utils/json/uint8.go | 2 +- avalanchego/utils/linkedhashmap/iterator.go | 70 + .../utils/linkedhashmap/linkedhashmap.go | 142 +- .../utils/linkedhashmap/linkedhashmap_test.go | 14 +- avalanchego/utils/logging/color.go | 2 +- avalanchego/utils/logging/config.go | 3 +- avalanchego/utils/logging/factory.go | 4 +- avalanchego/utils/logging/level.go | 2 +- avalanchego/utils/logging/log.go | 51 +- avalanchego/utils/logging/log_test.go | 4 +- avalanchego/utils/logging/logger.go | 13 +- avalanchego/utils/logging/mock_logger.go | 58 - avalanchego/utils/logging/sanitize.go | 2 +- avalanchego/utils/logging/test_log.go | 10 +- avalanchego/utils/math/averager.go | 2 +- avalanchego/utils/math/averager_heap.go | 3 +- avalanchego/utils/math/averager_heap_test.go | 2 +- avalanchego/utils/math/continuous_averager.go | 2 +- .../continuous_averager_benchmark_test.go | 2 +- .../utils/math/continuous_averager_test.go | 2 +- .../utils/math/meter/continuous_meter.go | 2 +- avalanchego/utils/math/meter/factory.go | 2 +- avalanchego/utils/math/meter/meter.go | 2 +- .../utils/math/meter/meter_benchmark_test.go | 2 +- avalanchego/utils/math/meter/meter_test.go | 2 +- avalanchego/utils/math/safe_math.go | 2 +- avalanchego/utils/math/safe_math_test.go | 2 +- avalanchego/utils/math/sync_averager.go | 2 +- avalanchego/utils/metric/api_interceptor.go | 2 +- avalanchego/utils/metric/averager.go | 2 +- avalanchego/utils/password/hash.go | 2 +- avalanchego/utils/password/hash_test.go | 2 +- avalanchego/utils/password/password.go | 2 +- avalanchego/utils/password/password_test.go | 2 +- avalanchego/utils/perms/chmod.go | 2 +- avalanchego/utils/perms/create.go | 2 +- avalanchego/utils/perms/perms.go | 2 +- avalanchego/utils/perms/write_file.go | 2 +- avalanchego/utils/profiler/continuous.go | 2 +- avalanchego/utils/profiler/profiler.go | 2 +- avalanchego/utils/profiler/profiler_test.go | 2 +- avalanchego/utils/resource/no_usage.go | 2 +- avalanchego/utils/resource/usage.go | 2 +- avalanchego/utils/resource/usage_test.go | 2 +- avalanchego/utils/rpc/json.go | 2 +- avalanchego/utils/rpc/options.go | 2 +- avalanchego/utils/rpc/requester.go | 2 +- avalanchego/utils/sampler/rand.go | 2 +- avalanchego/utils/sampler/uniform.go | 2 +- .../utils/sampler/uniform_benchmark_test.go | 2 +- avalanchego/utils/sampler/uniform_best.go | 2 +- avalanchego/utils/sampler/uniform_replacer.go | 2 +- avalanchego/utils/sampler/uniform_resample.go | 2 +- avalanchego/utils/sampler/uniform_test.go | 2 +- avalanchego/utils/sampler/weighted.go | 2 +- avalanchego/utils/sampler/weighted_array.go | 2 +- .../utils/sampler/weighted_benchmark_test.go | 2 +- avalanchego/utils/sampler/weighted_best.go | 2 +- avalanchego/utils/sampler/weighted_heap.go | 2 +- .../utils/sampler/weighted_heap_test.go | 2 +- avalanchego/utils/sampler/weighted_linear.go | 2 +- avalanchego/utils/sampler/weighted_test.go | 2 +- avalanchego/utils/sampler/weighted_uniform.go | 2 +- .../sampler/weighted_without_replacement.go | 2 +- ...hted_without_replacement_benchmark_test.go | 2 +- .../weighted_without_replacement_generic.go | 2 +- .../weighted_without_replacement_test.go | 2 +- avalanchego/utils/sorting.go | 2 +- avalanchego/utils/sorting_test.go | 2 +- avalanchego/utils/storage/storage_common.go | 2 +- avalanchego/utils/storage/storage_unix.go | 2 +- avalanchego/utils/storage/storage_windows.go | 2 +- avalanchego/utils/subprocess/linux_new.go | 2 +- avalanchego/utils/subprocess/non_linux_new.go | 2 +- .../utils/timer/adaptive_timeout_manager.go | 19 +- .../timer/adaptive_timeout_manager_test.go | 2 +- avalanchego/utils/timer/eta.go | 2 +- avalanchego/utils/timer/meter.go | 2 +- avalanchego/utils/timer/mockable/clock.go | 2 +- .../utils/timer/mockable/clock_test.go | 2 +- avalanchego/utils/timer/repeater.go | 79 - avalanchego/utils/timer/repeater_test.go | 27 - avalanchego/utils/timer/staged_timer.go | 5 +- avalanchego/utils/timer/staged_timer_test.go | 2 +- avalanchego/utils/timer/timed_meter.go | 81 - avalanchego/utils/timer/timeout_manager.go | 2 +- .../utils/timer/timeout_manager_test.go | 2 +- avalanchego/utils/timer/timer.go | 2 +- avalanchego/utils/timer/timer_test.go | 2 +- avalanchego/utils/ulimit/ulimit_bsd.go | 2 +- avalanchego/utils/ulimit/ulimit_darwin.go | 2 +- avalanchego/utils/ulimit/ulimit_test.go | 9 +- avalanchego/utils/ulimit/ulimit_unix.go | 2 +- avalanchego/utils/ulimit/ulimit_windows.go | 2 +- avalanchego/utils/units/avax.go | 2 +- avalanchego/utils/units/bytes.go | 2 +- avalanchego/utils/window/window.go | 85 +- avalanchego/utils/window/window_test.go | 32 +- avalanchego/utils/wrappers/closers.go | 2 +- avalanchego/utils/wrappers/errors.go | 2 +- avalanchego/utils/wrappers/packing.go | 2 +- avalanchego/utils/wrappers/packing_test.go | 2 +- avalanchego/utils/zero.go | 9 + avalanchego/version/application.go | 2 +- avalanchego/version/application_test.go | 2 +- avalanchego/version/compatibility.go | 2 +- avalanchego/version/compatibility_test.go | 2 +- avalanchego/version/constants.go | 53 +- avalanchego/version/parser.go | 2 +- avalanchego/version/parser_test.go | 2 +- avalanchego/version/string.go | 2 +- avalanchego/version/version.go | 2 +- avalanchego/version/version_test.go | 2 +- avalanchego/vms/avm/client.go | 2 +- avalanchego/vms/avm/client_test.go | 2 +- avalanchego/vms/avm/factory.go | 10 +- avalanchego/vms/avm/fx_test.go | 2 +- avalanchego/vms/avm/fxs/fx.go | 2 +- avalanchego/vms/avm/genesis.go | 2 +- avalanchego/vms/avm/health.go | 2 +- avalanchego/vms/avm/index_test.go | 2 +- avalanchego/vms/avm/metrics.go | 2 +- avalanchego/vms/avm/pubsub_filterer.go | 2 +- avalanchego/vms/avm/pubsub_filterer_test.go | 2 +- avalanchego/vms/avm/service.go | 2 +- avalanchego/vms/avm/service_test.go | 2 +- avalanchego/vms/avm/state_test.go | 2 +- avalanchego/vms/avm/states/state.go | 2 +- avalanchego/vms/avm/states/tx_state.go | 2 +- avalanchego/vms/avm/states/tx_state_test.go | 2 +- avalanchego/vms/avm/static_client.go | 2 +- avalanchego/vms/avm/static_service.go | 2 +- avalanchego/vms/avm/static_service_test.go | 2 +- avalanchego/vms/avm/tx_execute.go | 2 +- avalanchego/vms/avm/tx_init.go | 2 +- avalanchego/vms/avm/tx_semantic_verify.go | 8 +- .../vms/avm/tx_semantic_verify_test.go | 10 +- avalanchego/vms/avm/txs/base_tx.go | 2 +- avalanchego/vms/avm/txs/base_tx_test.go | 2 +- avalanchego/vms/avm/txs/codec.go | 2 +- avalanchego/vms/avm/txs/create_asset_tx.go | 2 +- .../vms/avm/txs/create_asset_tx_test.go | 2 +- avalanchego/vms/avm/txs/export_tx.go | 2 +- avalanchego/vms/avm/txs/export_tx_test.go | 2 +- avalanchego/vms/avm/txs/import_tx.go | 2 +- avalanchego/vms/avm/txs/import_tx_test.go | 2 +- avalanchego/vms/avm/txs/initial_state.go | 2 +- avalanchego/vms/avm/txs/initial_state_test.go | 2 +- avalanchego/vms/avm/txs/operation.go | 2 +- avalanchego/vms/avm/txs/operation_test.go | 2 +- avalanchego/vms/avm/txs/operation_tx.go | 2 +- avalanchego/vms/avm/txs/parser.go | 2 +- avalanchego/vms/avm/txs/tx.go | 2 +- avalanchego/vms/avm/txs/tx_test.go | 2 +- avalanchego/vms/avm/txs/visitor.go | 2 +- avalanchego/vms/avm/unique_tx.go | 2 +- avalanchego/vms/avm/vm.go | 6 +- avalanchego/vms/avm/vm_benchmark_test.go | 2 +- avalanchego/vms/avm/vm_test.go | 14 +- avalanchego/vms/avm/wallet_client.go | 2 +- avalanchego/vms/avm/wallet_service.go | 2 +- avalanchego/vms/avm/wallet_service_test.go | 2 +- avalanchego/vms/components/avax/addresses.go | 2 +- avalanchego/vms/components/avax/asset.go | 2 +- avalanchego/vms/components/avax/asset_test.go | 2 +- .../vms/components/avax/atomic_utxos.go | 2 +- avalanchego/vms/components/avax/base_tx.go | 2 +- .../vms/components/avax/flow_checker.go | 2 +- avalanchego/vms/components/avax/metadata.go | 2 +- .../vms/components/avax/metadata_test.go | 2 +- .../components/avax/mock_transferable_out.go | 89 + .../vms/components/avax/singleton_state.go | 2 +- .../components/avax/singleton_state_test.go | 2 +- avalanchego/vms/components/avax/state.go | 2 +- .../vms/components/avax/status_state.go | 2 +- .../vms/components/avax/status_state_test.go | 2 +- .../vms/components/avax/test_verifiable.go | 2 +- .../vms/components/avax/transferables.go | 2 +- .../vms/components/avax/transferables_test.go | 2 +- avalanchego/vms/components/avax/utxo.go | 2 +- .../vms/components/avax/utxo_fetching.go | 2 +- .../vms/components/avax/utxo_fetching_test.go | 2 +- avalanchego/vms/components/avax/utxo_id.go | 2 +- .../vms/components/avax/utxo_id_test.go | 2 +- avalanchego/vms/components/avax/utxo_state.go | 2 +- .../vms/components/avax/utxo_state_test.go | 2 +- avalanchego/vms/components/avax/utxo_test.go | 2 +- avalanchego/vms/components/chain/block.go | 2 +- avalanchego/vms/components/chain/state.go | 4 +- .../vms/components/chain/state_test.go | 2 +- avalanchego/vms/components/index/index.go | 2 +- avalanchego/vms/components/index/metrics.go | 2 +- avalanchego/vms/components/keystore/codec.go | 2 +- avalanchego/vms/components/keystore/user.go | 2 +- .../vms/components/keystore/user_test.go | 2 +- avalanchego/vms/components/state/builtin.go | 2 +- avalanchego/vms/components/state/state.go | 2 +- .../vms/components/state/state_test.go | 2 +- avalanchego/vms/components/state/types.go | 2 +- .../vms/components/verify/mock_verifiable.go | 48 + avalanchego/vms/components/verify/subnet.go | 2 +- .../vms/components/verify/subnet_test.go | 2 +- .../vms/components/verify/verification.go | 2 +- .../components/verify/verification_test.go | 2 +- avalanchego/vms/manager.go | 2 +- avalanchego/vms/metervm/batched_vm.go | 2 +- avalanchego/vms/metervm/block.go | 2 +- avalanchego/vms/metervm/block_metrics.go | 2 +- avalanchego/vms/metervm/block_vm.go | 2 +- avalanchego/vms/metervm/height_indexed_vm.go | 2 +- avalanchego/vms/metervm/metrics.go | 2 +- avalanchego/vms/metervm/state_syncable_vm.go | 2 +- avalanchego/vms/metervm/vertex_metrics.go | 2 +- avalanchego/vms/metervm/vertex_vm.go | 2 +- avalanchego/vms/nftfx/credential.go | 2 +- avalanchego/vms/nftfx/credential_test.go | 2 +- avalanchego/vms/nftfx/factory.go | 2 +- avalanchego/vms/nftfx/factory_test.go | 2 +- avalanchego/vms/nftfx/fx.go | 2 +- avalanchego/vms/nftfx/fx_test.go | 2 +- avalanchego/vms/nftfx/mint_operation.go | 2 +- avalanchego/vms/nftfx/mint_operation_test.go | 2 +- avalanchego/vms/nftfx/mint_output.go | 2 +- avalanchego/vms/nftfx/mint_output_test.go | 2 +- avalanchego/vms/nftfx/transfer_operation.go | 2 +- .../vms/nftfx/transfer_operation_test.go | 2 +- avalanchego/vms/nftfx/transfer_output.go | 2 +- avalanchego/vms/nftfx/transfer_output_test.go | 2 +- .../vms/platformvm/api/static_client.go | 2 +- .../vms/platformvm/api/static_service.go | 66 +- .../vms/platformvm/api/static_service_test.go | 22 +- .../vms/platformvm/blocks/abort_block.go | 69 + .../vms/platformvm/blocks/abort_block_test.go | 52 + .../vms/platformvm/blocks/atomic_block.go | 32 +- .../platformvm/blocks/atomic_block_test.go | 50 + avalanchego/vms/platformvm/blocks/block.go | 14 +- .../blocks/builder/apricot_builder.go | 113 + .../blocks/builder/apricot_builder_test.go | 360 +++ .../blocks/builder/banff_builder.go | 61 + .../blocks/builder/banff_builder_test.go | 511 ++++ .../vms/platformvm/blocks/builder/builder.go | 242 +- .../platformvm/blocks/builder/builder_test.go | 206 +- .../platformvm/blocks/builder/helpers_test.go | 29 +- .../vms/platformvm/blocks/builder/network.go | 2 +- .../platformvm/blocks/builder/network_test.go | 4 +- .../blocks/builder/standard_block_test.go | 9 +- avalanchego/vms/platformvm/blocks/codec.go | 37 +- .../vms/platformvm/blocks/commit_block.go | 69 + .../platformvm/blocks/commit_block_test.go | 52 + .../vms/platformvm/blocks/common_block.go | 9 +- .../platformvm/blocks/executor/acceptor.go | 260 +- .../blocks/executor/acceptor_test.go | 60 +- .../vms/platformvm/blocks/executor/backend.go | 37 +- .../blocks/executor/backend_test.go | 162 + .../vms/platformvm/blocks/executor/block.go | 85 +- .../platformvm/blocks/executor/block_test.go | 256 ++ .../blocks/executor/helpers_test.go | 529 ++++ .../vms/platformvm/blocks/executor/manager.go | 11 +- .../blocks/executor/manager_test.go | 2 +- .../vms/platformvm/blocks/executor/options.go | 95 + .../blocks/executor/options_test.go | 33 + .../blocks/executor/proposal_block_test.go | 1284 ++++++++ .../platformvm/blocks/executor/rejector.go | 42 +- .../blocks/executor/rejector_test.go | 32 +- .../blocks/executor/standard_block_test.go | 837 ++++++ .../platformvm/blocks/executor/verifier.go | 450 ++- .../blocks/executor/verifier_test.go | 853 +++++- .../vms/platformvm/blocks/mock_block.go | 31 +- .../vms/platformvm/blocks/option_blocks.go | 70 - avalanchego/vms/platformvm/blocks/parse.go | 2 +- .../vms/platformvm/blocks/parse_test.go | 180 +- .../vms/platformvm/blocks/proposal_block.go | 76 +- .../platformvm/blocks/proposal_block_test.go | 101 + .../vms/platformvm/blocks/standard_block.go | 68 +- .../platformvm/blocks/standard_block_test.go | 101 + avalanchego/vms/platformvm/blocks/visitor.go | 15 +- avalanchego/vms/platformvm/client.go | 43 +- ....go => client_permissionless_validator.go} | 59 +- avalanchego/vms/platformvm/config/config.go | 52 +- .../platformvm/docs/block_formation_logic.md | 101 + .../{README.md => docs/mempool_gossiping.md} | 8 +- avalanchego/vms/platformvm/docs/subnets.md | 36 + avalanchego/vms/platformvm/factory.go | 2 +- avalanchego/vms/platformvm/fx/fx.go | 2 +- avalanchego/vms/platformvm/genesis/codec.go | 29 +- avalanchego/vms/platformvm/genesis/genesis.go | 6 +- avalanchego/vms/platformvm/health.go | 2 +- avalanchego/vms/platformvm/message/codec.go | 2 +- avalanchego/vms/platformvm/message/handler.go | 2 +- .../vms/platformvm/message/handler_test.go | 2 +- avalanchego/vms/platformvm/message/message.go | 2 +- .../vms/platformvm/message/message_test.go | 2 +- .../vms/platformvm/metrics/block_metrics.go | 50 +- avalanchego/vms/platformvm/metrics/metrics.go | 2 +- avalanchego/vms/platformvm/metrics/no_op.go | 2 +- .../vms/platformvm/metrics/tx_metrics.go | 50 +- .../vms/platformvm/reward/calculator.go | 2 +- .../vms/platformvm/reward/calculator_test.go | 44 +- avalanchego/vms/platformvm/reward/config.go | 2 +- avalanchego/vms/platformvm/service.go | 475 +-- avalanchego/vms/platformvm/service_test.go | 44 +- avalanchego/vms/platformvm/signer/empty.go | 15 + .../vms/platformvm/signer/empty_test.go | 18 + .../platformvm/signer/proof_of_possession.go | 109 + .../signer/proof_of_possession_test.go | 55 + avalanchego/vms/platformvm/signer/signer.go | 18 + .../platformvm/stakeable/stakeable_lock.go | 2 +- avalanchego/vms/platformvm/state/diff.go | 68 +- avalanchego/vms/platformvm/state/diff_test.go | 37 +- .../vms/platformvm/state/empty_iterator.go | 2 +- .../platformvm/state/empty_iterator_test.go | 2 +- .../vms/platformvm/state/masked_iterator.go | 2 +- .../platformvm/state/masked_iterator_test.go | 2 +- .../vms/platformvm/state/merged_iterator.go | 2 +- .../platformvm/state/merged_iterator_test.go | 2 +- .../vms/platformvm/state/mock_chain.go | 453 +++ avalanchego/vms/platformvm/state/mock_diff.go | 232 +- .../platformvm/state/mock_staker_iterator.go | 23 +- .../vms/platformvm/state/mock_state.go | 835 ++---- .../vms/platformvm/state/priorities.go | 36 - .../platformvm/state/slice_iterator_test.go | 2 +- avalanchego/vms/platformvm/state/staker.go | 40 +- .../platformvm/state/staker_diff_iterator.go | 6 +- .../state/staker_diff_iterator_test.go | 25 +- .../vms/platformvm/state/staker_test.go | 123 +- avalanchego/vms/platformvm/state/stakers.go | 10 +- .../vms/platformvm/state/stakers_test.go | 5 +- avalanchego/vms/platformvm/state/state.go | 686 +++-- .../vms/platformvm/state/state_test.go | 10 +- .../vms/platformvm/state/tree_iterator.go | 4 +- .../platformvm/state/tree_iterator_test.go | 2 +- avalanchego/vms/platformvm/state/utxos.go | 2 +- avalanchego/vms/platformvm/state/versions.go | 2 +- .../platformvm/status/blockchain_status.go | 2 +- .../status/blockchain_status_test.go | 2 +- avalanchego/vms/platformvm/status/status.go | 2 +- .../vms/platformvm/status/status_test.go | 2 +- .../vms/platformvm/txs/add_delegator_test.go | 18 +- .../vms/platformvm/txs/add_delegator_tx.go | 45 +- .../txs/add_permissionless_delegator_tx.go | 135 + .../add_permissionless_delegator_tx_test.go | 393 +++ .../txs/add_permissionless_validator_tx.go | 179 ++ .../add_permissionless_validator_tx_test.go | 501 ++++ .../txs/add_subnet_validator_test.go | 34 +- .../platformvm/txs/add_subnet_validator_tx.go | 33 +- .../vms/platformvm/txs/add_validator_test.go | 26 +- .../vms/platformvm/txs/add_validator_tx.go | 50 +- .../vms/platformvm/txs/advance_time_tx.go | 2 +- avalanchego/vms/platformvm/txs/base_tx.go | 4 +- .../vms/platformvm/txs/base_tx_test.go | 2 +- .../vms/platformvm/txs/builder/builder.go | 65 +- .../platformvm/txs/builder/mock_builder.go | 188 ++ avalanchego/vms/platformvm/txs/codec.go | 42 +- .../vms/platformvm/txs/create_chain_test.go | 2 +- .../vms/platformvm/txs/create_chain_tx.go | 6 +- .../vms/platformvm/txs/create_subnet_tx.go | 8 +- .../txs/executor/advance_time_test.go | 376 ++- .../txs/executor/atomic_tx_executor.go | 14 +- .../vms/platformvm/txs/executor/backend.go | 2 +- .../txs/executor/create_chain_test.go | 2 +- .../txs/executor/create_subnet_test.go | 2 +- .../platformvm/txs/executor/export_test.go | 2 +- .../platformvm/txs/executor/helpers_test.go | 11 +- .../platformvm/txs/executor/import_test.go | 10 +- .../txs/executor/inflation_settings.go | 39 +- .../txs/executor/proposal_tx_executor.go | 944 ++---- .../txs/executor/proposal_tx_executor_test.go | 407 ++- .../txs/executor/reward_validator_test.go | 250 +- .../txs/executor/staker_tx_verification.go | 804 +++++ .../executor/staker_tx_verification_test.go | 758 +++++ .../txs/executor/standard_tx_executor.go | 319 +- .../txs/executor/standard_tx_executor_test.go | 1808 +++++++++++ .../platformvm/txs/executor/state_changes.go | 231 ++ .../txs/executor/subnet_tx_verification.go | 90 + .../txs/executor/tx_mempool_verifier.go | 53 +- avalanchego/vms/platformvm/txs/export_tx.go | 5 +- avalanchego/vms/platformvm/txs/import_tx.go | 5 +- .../vms/platformvm/txs/mempool/issuer.go | 85 + .../vms/platformvm/txs/mempool/mempool.go | 205 +- .../platformvm/txs/mempool/mempool_test.go | 238 +- .../platformvm/txs/mempool/mock_mempool.go | 151 +- .../vms/platformvm/txs/mempool/remover.go | 78 + avalanchego/vms/platformvm/txs/mock_staker.go | 134 + avalanchego/vms/platformvm/txs/priorities.go | 46 + .../txs/remove_subnet_validator_tx.go | 56 + .../txs/remove_subnet_validator_tx_test.go | 161 + .../vms/platformvm/txs/reward_validator_tx.go | 4 +- avalanchego/vms/platformvm/txs/staker_tx.go | 46 +- .../vms/platformvm/txs/transform_subnet_tx.go | 170 ++ .../txs/transform_subnet_tx_test.go | 440 +++ avalanchego/vms/platformvm/txs/tx.go | 13 +- .../vms/platformvm/txs/txheap/by_age.go | 2 +- .../vms/platformvm/txs/txheap/by_end_time.go | 8 +- .../platformvm/txs/txheap/by_end_time_test.go | 2 +- .../platformvm/txs/txheap/by_start_time.go | 8 +- .../txs/txheap/by_start_time_test.go | 2 +- avalanchego/vms/platformvm/txs/txheap/heap.go | 3 +- .../vms/platformvm/txs/txheap/with_metrics.go | 2 +- avalanchego/vms/platformvm/txs/unsigned_tx.go | 5 +- avalanchego/vms/platformvm/txs/visitor.go | 6 +- avalanchego/vms/platformvm/utxo/handler.go | 2 +- .../vms/platformvm/utxo/handler_test.go | 2 +- .../vms/platformvm/utxo/mock_verifier.go | 67 + avalanchego/vms/platformvm/validator/heap.go | 24 - .../platformvm/validator/subnet_validator.go | 4 +- .../vms/platformvm/validator/validator.go | 2 +- .../platformvm/validator/validator_test.go | 2 +- avalanchego/vms/platformvm/vm.go | 13 +- .../vms/platformvm/vm_regression_test.go | 80 +- avalanchego/vms/platformvm/vm_test.go | 697 ++--- avalanchego/vms/propertyfx/burn_operation.go | 2 +- .../vms/propertyfx/burn_operation_test.go | 2 +- avalanchego/vms/propertyfx/credential.go | 2 +- avalanchego/vms/propertyfx/credential_test.go | 2 +- avalanchego/vms/propertyfx/factory.go | 2 +- avalanchego/vms/propertyfx/factory_test.go | 2 +- avalanchego/vms/propertyfx/fx.go | 2 +- avalanchego/vms/propertyfx/fx_test.go | 2 +- avalanchego/vms/propertyfx/mint_operation.go | 2 +- .../vms/propertyfx/mint_operation_test.go | 2 +- avalanchego/vms/propertyfx/mint_output.go | 2 +- .../vms/propertyfx/mint_output_test.go | 2 +- avalanchego/vms/propertyfx/owned_output.go | 2 +- .../vms/propertyfx/owned_output_test.go | 2 +- avalanchego/vms/proposervm/batched_vm.go | 9 +- avalanchego/vms/proposervm/batched_vm_test.go | 4 +- avalanchego/vms/proposervm/block.go | 57 +- avalanchego/vms/proposervm/block/block.go | 2 +- .../vms/proposervm/block/block_test.go | 24 +- avalanchego/vms/proposervm/block/build.go | 61 +- .../vms/proposervm/block/build_test.go | 6 +- avalanchego/vms/proposervm/block/codec.go | 39 +- avalanchego/vms/proposervm/block/header.go | 2 +- .../vms/proposervm/block/header_test.go | 2 +- avalanchego/vms/proposervm/block/option.go | 2 +- .../vms/proposervm/block/option_test.go | 2 +- avalanchego/vms/proposervm/block/parse.go | 29 +- .../vms/proposervm/block/parse_test.go | 19 +- avalanchego/vms/proposervm/block_server.go | 2 +- .../vms/proposervm/height_indexed_vm.go | 2 +- .../vms/proposervm/indexer/block_server.go | 2 +- .../proposervm/indexer/block_server_test.go | 2 +- .../vms/proposervm/indexer/height_indexer.go | 2 +- .../proposervm/indexer/height_indexer_test.go | 8 +- avalanchego/vms/proposervm/post_fork_block.go | 2 +- .../vms/proposervm/post_fork_block_test.go | 44 +- .../vms/proposervm/post_fork_option.go | 2 +- .../vms/proposervm/post_fork_option_test.go | 6 +- avalanchego/vms/proposervm/pre_fork_block.go | 7 +- .../vms/proposervm/pre_fork_block_test.go | 6 +- .../vms/proposervm/proposer/validators.go | 2 +- .../vms/proposervm/proposer/windower.go | 2 +- .../vms/proposervm/proposer/windower_test.go | 2 +- .../vms/proposervm/scheduler/scheduler.go | 4 +- .../proposervm/scheduler/scheduler_test.go | 2 +- .../proposervm/state/block_height_index.go | 2 +- .../state/block_height_index_test.go | 2 +- .../vms/proposervm/state/block_state.go | 8 +- .../vms/proposervm/state/block_state_test.go | 4 +- .../vms/proposervm/state/chain_state.go | 2 +- .../vms/proposervm/state/chain_state_test.go | 2 +- avalanchego/vms/proposervm/state/codec.go | 2 +- .../vms/proposervm/state/mock_state.go | 285 ++ avalanchego/vms/proposervm/state/state.go | 2 +- .../vms/proposervm/state/state_test.go | 2 +- avalanchego/vms/proposervm/state_summary.go | 2 +- .../vms/proposervm/state_syncable_vm.go | 2 +- .../vms/proposervm/state_syncable_vm_test.go | 20 +- avalanchego/vms/proposervm/summary/build.go | 2 +- .../vms/proposervm/summary/build_test.go | 2 +- avalanchego/vms/proposervm/summary/codec.go | 2 +- avalanchego/vms/proposervm/summary/parse.go | 2 +- .../vms/proposervm/summary/parse_test.go | 2 +- .../vms/proposervm/summary/state_summary.go | 2 +- avalanchego/vms/proposervm/tree/tree.go | 2 +- avalanchego/vms/proposervm/tree/tree_test.go | 2 +- avalanchego/vms/proposervm/vm.go | 90 +- .../vms/proposervm/vm_byzantine_test.go | 10 +- avalanchego/vms/proposervm/vm_test.go | 139 +- avalanchego/vms/registry/mock_vm_getter.go | 2 +- .../vms/registry/mock_vm_registerer.go | 2 +- avalanchego/vms/registry/mock_vm_registry.go | 2 +- avalanchego/vms/registry/vm_getter.go | 2 +- avalanchego/vms/registry/vm_getter_test.go | 2 +- avalanchego/vms/registry/vm_registerer.go | 2 +- .../vms/registry/vm_registerer_test.go | 2 +- avalanchego/vms/registry/vm_registry.go | 2 +- avalanchego/vms/registry/vm_registry_test.go | 2 +- avalanchego/vms/rpcchainvm/errors.go | 2 +- avalanchego/vms/rpcchainvm/factory.go | 2 +- .../vms/rpcchainvm/ghttp/gconn/conn_client.go | 2 +- .../vms/rpcchainvm/ghttp/gconn/conn_server.go | 2 +- .../rpcchainvm/ghttp/greader/reader_client.go | 2 +- .../rpcchainvm/ghttp/greader/reader_server.go | 2 +- .../ghttp/gresponsewriter/locked_writer.go | 2 +- .../ghttp/gresponsewriter/writer_client.go | 2 +- .../ghttp/gresponsewriter/writer_server.go | 2 +- .../rpcchainvm/ghttp/gwriter/writer_client.go | 2 +- .../rpcchainvm/ghttp/gwriter/writer_server.go | 2 +- .../vms/rpcchainvm/ghttp/http_client.go | 2 +- .../vms/rpcchainvm/ghttp/http_server.go | 2 +- avalanchego/vms/rpcchainvm/ghttp/http_test.go | 2 +- .../vms/rpcchainvm/grpcutils/listener.go | 2 +- .../vms/rpcchainvm/grpcutils/server_closer.go | 2 +- avalanchego/vms/rpcchainvm/grpcutils/util.go | 2 +- .../gsubnetlookup/subnet_lookup_client.go | 2 +- .../gsubnetlookup/subnet_lookup_server.go | 2 +- .../rpcchainvm/messenger/messenger_client.go | 2 +- .../rpcchainvm/messenger/messenger_server.go | 2 +- avalanchego/vms/rpcchainvm/plugin_test.go | 2 +- .../vms/rpcchainvm/state_syncable_vm_test.go | 8 +- avalanchego/vms/rpcchainvm/vm.go | 4 +- avalanchego/vms/rpcchainvm/vm_client.go | 2 +- avalanchego/vms/rpcchainvm/vm_server.go | 2 +- avalanchego/vms/rpcchainvm/vm_test.go | 4 +- avalanchego/vms/secp256k1fx/credential.go | 2 +- .../vms/secp256k1fx/credential_test.go | 2 +- avalanchego/vms/secp256k1fx/factory.go | 2 +- avalanchego/vms/secp256k1fx/factory_test.go | 2 +- avalanchego/vms/secp256k1fx/fx.go | 2 +- avalanchego/vms/secp256k1fx/fx_test.go | 2 +- avalanchego/vms/secp256k1fx/input.go | 2 +- avalanchego/vms/secp256k1fx/input_test.go | 2 +- avalanchego/vms/secp256k1fx/keychain.go | 2 +- avalanchego/vms/secp256k1fx/keychain_test.go | 2 +- avalanchego/vms/secp256k1fx/mint_operation.go | 2 +- .../vms/secp256k1fx/mint_operation_test.go | 2 +- avalanchego/vms/secp256k1fx/mint_output.go | 2 +- .../vms/secp256k1fx/mint_output_test.go | 2 +- avalanchego/vms/secp256k1fx/output_owners.go | 2 +- .../vms/secp256k1fx/output_owners_test.go | 2 +- avalanchego/vms/secp256k1fx/transfer_input.go | 2 +- .../vms/secp256k1fx/transfer_input_test.go | 2 +- .../vms/secp256k1fx/transfer_output.go | 2 +- .../vms/secp256k1fx/transfer_output_test.go | 2 +- avalanchego/vms/secp256k1fx/tx.go | 2 +- avalanchego/vms/secp256k1fx/vm.go | 2 +- avalanchego/vms/types/blob_data.go | 2 +- avalanchego/wallet/chain/p/backend.go | 2 +- avalanchego/wallet/chain/p/backend_visitor.go | 18 +- avalanchego/wallet/chain/p/builder.go | 332 +- .../wallet/chain/p/builder_with_options.go | 95 +- avalanchego/wallet/chain/p/context.go | 62 +- avalanchego/wallet/chain/p/signer.go | 12 +- avalanchego/wallet/chain/p/signer_visitor.go | 51 +- avalanchego/wallet/chain/p/wallet.go | 196 +- .../wallet/chain/p/wallet_with_options.go | 87 +- avalanchego/wallet/chain/x/backend.go | 2 +- avalanchego/wallet/chain/x/builder.go | 12 +- .../wallet/chain/x/builder_with_options.go | 2 +- avalanchego/wallet/chain/x/constants.go | 2 +- avalanchego/wallet/chain/x/context.go | 2 +- avalanchego/wallet/chain/x/signer.go | 2 +- avalanchego/wallet/chain/x/wallet.go | 2 +- .../wallet/chain/x/wallet_with_options.go | 2 +- avalanchego/wallet/subnet/primary/api.go | 2 +- .../wallet/subnet/primary/common/options.go | 2 +- .../wallet/subnet/primary/common/spend.go | 2 +- .../wallet/subnet/primary/example_test.go | 115 +- avalanchego/wallet/subnet/primary/utxos.go | 2 +- avalanchego/wallet/subnet/primary/wallet.go | 2 +- coreth/RELEASES.md | 11 + coreth/accounts/abi/abi.go | 2 +- coreth/accounts/abi/argument.go | 6 +- .../accounts/abi/bind/backends/simulated.go | 33 +- coreth/accounts/abi/reflect.go | 2 +- coreth/accounts/abi/reflect_test.go | 2 +- coreth/accounts/abi/unpack.go | 6 +- coreth/accounts/abi/unpack_test.go | 5 + .../accounts/keystore/account_cache_test.go | 2 +- coreth/accounts/keystore/file_cache.go | 4 +- coreth/accounts/keystore/keystore_test.go | 2 +- coreth/consensus/dummy/README.md | 2 +- coreth/core/blockchain.go | 102 +- coreth/core/blockchain_reader.go | 3 + coreth/core/bloom_indexer.go | 2 +- coreth/core/genesis.go | 27 +- coreth/core/genesis_test.go | 72 +- coreth/core/rawdb/accessors_chain_test.go | 2 +- coreth/core/rawdb/accessors_state_sync.go | 7 +- coreth/core/state/database.go | 28 +- coreth/core/state/metrics.go | 12 +- coreth/core/state/pruner/pruner.go | 4 +- coreth/core/state/snapshot/generate.go | 4 +- coreth/core/state/snapshot/generate_test.go | 66 +- coreth/core/state/snapshot/iterator_fast.go | 4 +- coreth/core/state/snapshot/snapshot_test.go | 2 +- coreth/core/state/state_object.go | 11 +- coreth/core/state/statedb.go | 71 +- coreth/core/state/statedb_test.go | 42 +- coreth/core/state/trie_prefetcher.go | 9 +- coreth/core/state_processor.go | 6 +- coreth/core/state_processor_test.go | 2 +- coreth/core/state_transition.go | 9 +- coreth/core/tx_journal.go | 9 +- coreth/core/tx_pool.go | 7 + coreth/core/types/block.go | 5 +- coreth/core/types/bloom9.go | 2 +- coreth/core/types/gen_header_json.go | 6 + coreth/core/types/gen_header_rlp.go | 18 +- coreth/core/vm/contracts.go | 25 + coreth/core/vm/contracts_stateful_test.go | 2 +- coreth/core/vm/evm.go | 38 + coreth/core/vm/instructions.go | 30 +- coreth/core/vm/runtime/runtime_test.go | 6 +- coreth/eth/api.go | 42 +- coreth/eth/api_backend.go | 22 +- coreth/eth/backend.go | 11 +- coreth/eth/ethconfig/config.go | 6 +- coreth/eth/filters/api.go | 32 +- coreth/eth/filters/filter.go | 112 +- coreth/eth/filters/filter_system.go | 95 +- coreth/eth/state_accessor.go | 2 +- coreth/eth/tracers/api.go | 23 +- coreth/eth/tracers/api_test.go | 2 +- .../internal/tracetest/calltrace_test.go | 15 +- .../testdata/call_tracer/simple_onlytop.json | 72 + coreth/eth/tracers/native/4byte.go | 4 +- coreth/eth/tracers/native/call.go | 21 +- coreth/eth/tracers/native/noop.go | 4 +- coreth/eth/tracers/native/prestate.go | 4 +- coreth/eth/tracers/native/revertreason.go | 4 +- coreth/eth/tracers/native/tracer.go | 7 +- coreth/eth/tracers/tracers.go | 6 +- coreth/ethclient/ethclient.go | 33 + coreth/ethdb/memorydb/memorydb.go | 2 +- coreth/go.mod | 7 +- coreth/go.sum | 13 +- coreth/interfaces/interfaces.go | 9 + coreth/internal/ethapi/api.go | 71 +- coreth/internal/ethapi/backend.go | 16 +- coreth/internal/ethapi/transaction_args.go | 127 +- .../internal/ethapi/transaction_args_test.go | 257 ++ coreth/metrics/gauge_float64_test.go | 2 +- coreth/metrics/gauge_test.go | 2 +- coreth/metrics/runtime_test.go | 3 + coreth/miner/worker.go | 5 + coreth/params/config.go | 523 ++-- coreth/params/config_test.go | 82 +- coreth/params/version.go | 2 +- coreth/plugin/evm/atomic_backend.go | 423 +++ coreth/plugin/evm/atomic_state.go | 96 + coreth/plugin/evm/atomic_syncer.go | 66 +- coreth/plugin/evm/atomic_syncer_test.go | 20 +- coreth/plugin/evm/atomic_trie.go | 489 +-- .../plugin/evm/atomic_trie_iterator_test.go | 44 +- coreth/plugin/evm/atomic_trie_test.go | 202 +- coreth/plugin/evm/atomic_tx_repository.go | 105 +- .../plugin/evm/atomic_tx_repository_test.go | 29 +- coreth/plugin/evm/block.go | 125 +- coreth/plugin/evm/block_builder.go | 9 +- coreth/plugin/evm/block_verification.go | 11 + coreth/plugin/evm/export_tx.go | 16 +- coreth/plugin/evm/export_tx_test.go | 313 +- coreth/plugin/evm/import_tx.go | 14 +- coreth/plugin/evm/import_tx_test.go | 90 +- coreth/plugin/evm/log.go | 2 + coreth/plugin/evm/message/handler.go | 1 - coreth/plugin/evm/syncervm_client.go | 14 +- coreth/plugin/evm/syncervm_test.go | 10 +- coreth/plugin/evm/tx_test.go | 13 +- coreth/plugin/evm/version.go | 2 +- coreth/plugin/evm/vm.go | 242 +- coreth/plugin/evm/vm_extra_state_root_test.go | 267 ++ coreth/plugin/evm/vm_test.go | 49 +- coreth/rpc/http.go | 15 +- coreth/rpc/server.go | 2 +- coreth/scripts/geth-allowed-packages.txt | 15 + coreth/scripts/lint_allowed_geth_imports.sh | 16 + coreth/scripts/versions.sh | 4 +- coreth/sync/statesync/sync_test.go | 28 +- coreth/tests/init.go | 22 +- coreth/trie/committer.go | 159 +- coreth/trie/database.go | 272 +- coreth/trie/database_test.go | 64 - coreth/trie/hasher.go | 2 +- coreth/trie/iterator.go | 3 +- coreth/trie/iterator_test.go | 60 +- coreth/trie/node.go | 30 +- coreth/trie/node_test.go | 121 + coreth/trie/nodeset.go | 104 + coreth/trie/preimages.go | 107 + coreth/trie/proof.go | 16 +- coreth/trie/proof_test.go | 32 +- coreth/trie/secure_trie.go | 125 +- coreth/trie/secure_trie_test.go | 31 +- coreth/trie/sync_test.go | 18 +- coreth/trie/test_trie.go | 11 +- coreth/trie/trie.go | 118 +- coreth/trie/trie_test.go | 144 +- coreth/trie/util_test.go | 11 +- coreth/vmerrs/vmerrs.go | 2 + 1326 files changed, 37719 insertions(+), 11407 deletions(-) delete mode 100644 avalanchego/ids/queue.go delete mode 100644 avalanchego/ids/queue_test.go create mode 100644 avalanchego/message/inbound_msg_builder_proto.go create mode 100644 avalanchego/message/inbound_msg_builder_proto_test.go create mode 100644 avalanchego/message/messages_benchmark_test.go create mode 100644 avalanchego/message/messages_test.go create mode 100644 avalanchego/message/outbound_msg_builder_proto.go create mode 100644 avalanchego/message/outbound_msg_builder_proto_test.go create mode 100644 avalanchego/message/test_message.go create mode 100644 avalanchego/proto/buf.md create mode 100644 avalanchego/proto/p2p/p2p.proto create mode 100644 avalanchego/proto/pb/p2p/p2p.pb.go create mode 100644 avalanchego/snow/consensus/snowman/mock_block.go create mode 100644 avalanchego/snow/validators/custom_test.go create mode 100644 avalanchego/tests/e2e/banff/suites.go delete mode 100644 avalanchego/tests/e2e/blueberry/suites.go create mode 100644 avalanchego/tests/e2e/p/permissionless_subnets.go create mode 100644 avalanchego/tests/e2e/p/workflow.go delete mode 100644 avalanchego/tests/e2e/whitelist-vtx/suites.go create mode 100644 avalanchego/tests/e2e/x/whitelist-vtx/suites.go create mode 100644 avalanchego/utils/buffer/unbounded_queue.go create mode 100644 avalanchego/utils/buffer/unbounded_queue_test.go create mode 100644 avalanchego/utils/crypto/bls/bls_test.go create mode 100644 avalanchego/utils/crypto/bls/public.go create mode 100644 avalanchego/utils/crypto/bls/public_test.go create mode 100644 avalanchego/utils/crypto/bls/secret.go create mode 100644 avalanchego/utils/crypto/bls/secret_test.go create mode 100644 avalanchego/utils/crypto/bls/signature.go create mode 100644 avalanchego/utils/crypto/bls/signature_test.go create mode 100644 avalanchego/utils/linkedhashmap/iterator.go delete mode 100644 avalanchego/utils/timer/repeater.go delete mode 100644 avalanchego/utils/timer/repeater_test.go delete mode 100644 avalanchego/utils/timer/timed_meter.go create mode 100644 avalanchego/utils/zero.go create mode 100644 avalanchego/vms/components/avax/mock_transferable_out.go create mode 100644 avalanchego/vms/components/verify/mock_verifiable.go create mode 100644 avalanchego/vms/platformvm/blocks/abort_block.go create mode 100644 avalanchego/vms/platformvm/blocks/abort_block_test.go create mode 100644 avalanchego/vms/platformvm/blocks/atomic_block_test.go create mode 100644 avalanchego/vms/platformvm/blocks/builder/apricot_builder.go create mode 100644 avalanchego/vms/platformvm/blocks/builder/apricot_builder_test.go create mode 100644 avalanchego/vms/platformvm/blocks/builder/banff_builder.go create mode 100644 avalanchego/vms/platformvm/blocks/builder/banff_builder_test.go create mode 100644 avalanchego/vms/platformvm/blocks/commit_block.go create mode 100644 avalanchego/vms/platformvm/blocks/commit_block_test.go create mode 100644 avalanchego/vms/platformvm/blocks/executor/backend_test.go create mode 100644 avalanchego/vms/platformvm/blocks/executor/block_test.go create mode 100644 avalanchego/vms/platformvm/blocks/executor/helpers_test.go create mode 100644 avalanchego/vms/platformvm/blocks/executor/options.go create mode 100644 avalanchego/vms/platformvm/blocks/executor/options_test.go create mode 100644 avalanchego/vms/platformvm/blocks/executor/proposal_block_test.go create mode 100644 avalanchego/vms/platformvm/blocks/executor/standard_block_test.go delete mode 100644 avalanchego/vms/platformvm/blocks/option_blocks.go create mode 100644 avalanchego/vms/platformvm/blocks/proposal_block_test.go create mode 100644 avalanchego/vms/platformvm/blocks/standard_block_test.go rename avalanchego/vms/platformvm/{client_primary_validator.go => client_permissionless_validator.go} (59%) create mode 100644 avalanchego/vms/platformvm/docs/block_formation_logic.md rename avalanchego/vms/platformvm/{README.md => docs/mempool_gossiping.md} (89%) create mode 100644 avalanchego/vms/platformvm/docs/subnets.md create mode 100644 avalanchego/vms/platformvm/signer/empty.go create mode 100644 avalanchego/vms/platformvm/signer/empty_test.go create mode 100644 avalanchego/vms/platformvm/signer/proof_of_possession.go create mode 100644 avalanchego/vms/platformvm/signer/proof_of_possession_test.go create mode 100644 avalanchego/vms/platformvm/signer/signer.go create mode 100644 avalanchego/vms/platformvm/state/mock_chain.go delete mode 100644 avalanchego/vms/platformvm/state/priorities.go create mode 100644 avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx.go create mode 100644 avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx_test.go create mode 100644 avalanchego/vms/platformvm/txs/add_permissionless_validator_tx.go create mode 100644 avalanchego/vms/platformvm/txs/add_permissionless_validator_tx_test.go create mode 100644 avalanchego/vms/platformvm/txs/builder/mock_builder.go create mode 100644 avalanchego/vms/platformvm/txs/executor/staker_tx_verification.go create mode 100644 avalanchego/vms/platformvm/txs/executor/staker_tx_verification_test.go create mode 100644 avalanchego/vms/platformvm/txs/executor/standard_tx_executor_test.go create mode 100644 avalanchego/vms/platformvm/txs/executor/state_changes.go create mode 100644 avalanchego/vms/platformvm/txs/executor/subnet_tx_verification.go create mode 100644 avalanchego/vms/platformvm/txs/mempool/issuer.go create mode 100644 avalanchego/vms/platformvm/txs/mempool/remover.go create mode 100644 avalanchego/vms/platformvm/txs/mock_staker.go create mode 100644 avalanchego/vms/platformvm/txs/priorities.go create mode 100644 avalanchego/vms/platformvm/txs/remove_subnet_validator_tx.go create mode 100644 avalanchego/vms/platformvm/txs/remove_subnet_validator_tx_test.go create mode 100644 avalanchego/vms/platformvm/txs/transform_subnet_tx.go create mode 100644 avalanchego/vms/platformvm/txs/transform_subnet_tx_test.go create mode 100644 avalanchego/vms/platformvm/utxo/mock_verifier.go delete mode 100644 avalanchego/vms/platformvm/validator/heap.go create mode 100644 avalanchego/vms/proposervm/state/mock_state.go create mode 100644 coreth/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json create mode 100644 coreth/internal/ethapi/transaction_args_test.go create mode 100644 coreth/plugin/evm/atomic_backend.go create mode 100644 coreth/plugin/evm/atomic_state.go create mode 100644 coreth/plugin/evm/vm_extra_state_root_test.go create mode 100644 coreth/scripts/geth-allowed-packages.txt create mode 100644 coreth/scripts/lint_allowed_geth_imports.sh create mode 100644 coreth/trie/nodeset.go create mode 100644 coreth/trie/preimages.go diff --git a/avalanchego/.golangci.yml b/avalanchego/.golangci.yml index 632234d0..ffdec4c5 100644 --- a/avalanchego/.golangci.yml +++ b/avalanchego/.golangci.yml @@ -15,7 +15,6 @@ linters: disable-all: true enable: - asciicheck - - deadcode - depguard - errcheck - exportloopref @@ -37,7 +36,6 @@ linters: - unconvert - unparam - unused - - varcheck - unconvert - whitespace - staticcheck diff --git a/avalanchego/LICENSE.header b/avalanchego/LICENSE.header index d919afd2..f72620fe 100644 --- a/avalanchego/LICENSE.header +++ b/avalanchego/LICENSE.header @@ -1,2 +1,2 @@ -Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -See the file LICENSE for licensing terms. \ No newline at end of file +Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +See the file LICENSE for licensing terms. diff --git a/avalanchego/README.md b/avalanchego/README.md index a87fbef3..44a0ba37 100644 --- a/avalanchego/README.md +++ b/avalanchego/README.md @@ -17,7 +17,7 @@ The minimum recommended hardware specification for nodes connected to Mainnet is - CPU: Equivalent of 8 AWS vCPU - RAM: 16 GiB - Storage: 1 TiB -- OS: Ubuntu 18.04/20.04 or macOS >= 10.15 (Catalina) +- OS: Ubuntu 20.04 or macOS >= 12 - Network: Reliable IPv4 or IPv6 network connection, with an open public port. If you plan to build AvalancheGo from source, you will also need the following software: @@ -59,15 +59,6 @@ Install AvalancheGo using an `apt` repository. If you have already added the APT repository, you do not need to add it again. -To add the repository on Ubuntu 18.04 (Bionic), run: - -```sh -sudo su - -wget -O - https://downloads.avax.network/avalanchego.gpg.key | apt-key add - -echo "deb https://downloads.avax.network/apt bionic main" > /etc/apt/sources.list.d/avalanche.list -exit -``` - To add the repository on Ubuntu 20.04 (Focal), run: ```sh @@ -160,7 +151,7 @@ To regenerate the protobuf go code, run `scripts/protobuf_codegen.sh` from the r This should only be necessary when upgrading protobuf versions or modifying .proto definition files. -To use this script, you must have [buf](https://docs.buf.build/installation) (v1.4.0), protoc-gen-go (v1.28.0) and protoc-gen-go-grpc (v1.2.0) installed. +To use this script, you must have [buf](https://docs.buf.build/installation) (v1.7.0), protoc-gen-go (v1.28.0) and protoc-gen-go-grpc (v1.2.0) installed. To install the buf dependencies: diff --git a/avalanchego/RELEASES.md b/avalanchego/RELEASES.md index d4d89cfa..a8ee5fc3 100644 --- a/avalanchego/RELEASES.md +++ b/avalanchego/RELEASES.md @@ -1,5 +1,233 @@ # Release Notes +## [v1.9.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.9.0) + +This upgrade adds support for creating Proof-of-Stake Subnets. + +This version is not backwards compatible. The changes in the upgrade go into effect at 12 PM EDT, October 18th 2022 on Mainnet. + +**All Mainnet nodes should upgrade before 12 PM EDT, October 18th 2022.** + +The supported plugin version is `17`. + +### Upgrades + +- Activated P2P serialization format change to Protobuf +- Activated non-AVAX `ImportTx`/`ExportTx`s to/from the P-chain +- Activated `Banff*` blocks on the P-chain +- Deactivated `Apricot*` blocks on the P-chain +- Activated `RemoveSubnetValidatorTx`s on the P-chain +- Activated `TransformSubnetTx`s on the P-chain +- Activated `AddPermissionlessValidatorTx`s on the P-chain +- Activated `AddPermissionlessDelegatorTx`s on the P-chain +- Deactivated ANT `ImportTx`/`ExportTx`s on the C-chain +- Deactivated ANT precompiles on the C-chain + +### Deprecations + +- Ubuntu 18.04 releases are deprecated and will not be provided for `>=v1.9.1` + +### Miscellaneous + +- Fixed locked input signing in the P-chain wallet +- Removed assertions from the logger interface +- Removed `--assertions-enabled` flag +- Fixed typo in `--bootstrap-max-time-get-ancestors` flag +- Standardized exported P-Chain codec usage +- Improved isolation and execution of the E2E tests +- Updated the linked hashmap implementation to use generics + +## [v1.8.6](https://github.com/ava-labs/avalanchego/releases/tag/v1.8.6) + +This version is backwards compatible to [v1.8.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.8.0). It is optional, but encouraged. The supported plugin version is `16`. + +### BLS + +- Added BLS key file at `--staking-signer-key-file` +- Exposed BLS proof of possession in the `info.getNodeID` API +- Added BLS proof of possession to `AddPermissionlessValidatorTx`s for the Primary Network + +The default value of `--staking-signer-key-file` is `~/.avalanchego/staking/signer.key`. If the key file doesn't exist, it will be populated with a new key. + +### Networking + +- Added P2P proto support to be activated in a future release +- Fixed inbound bandwidth spike after leaving the validation set +- Removed support for `ChitsV2` messages +- Removed `ContainerID`s from `Put` and `PushQuery` messages +- Added `pending_timeouts` metric to track the number of active timeouts a node is tracking +- Fixed overflow in gzip decompression +- Optimized memory usage in `peer.MessageQueue` + +### Miscellaneous + +- Fixed bootstrapping ETA metric +- Removed unused `unknown_txs_count` metric +- Replaced duplicated code with generic implementations + +### Coreth + +- Added failure reason to bad block API + +## [v1.8.5](https://github.com/ava-labs/avalanchego/releases/tag/v1.8.5) + +Please upgrade your node as soon as possible. + +The supported plugin version is `16`. + +### Fixes + +- Fixed stale block reference by evicting blocks upon successful verification + +### [Coreth](https://medium.com/avalancheavax/apricot-phase-6-native-asset-call-deprecation-a7b7a77b850a) + +- Removed check for Apricot Phase6 incompatible fork to unblock nodes that did not upgrade ahead of the activation time + +## [v1.8.4](https://github.com/ava-labs/avalanchego/releases/tag/v1.8.4) + +Please upgrade your node as soon as possible. + +The supported plugin version is `16`. + +### Caching + +- Added temporarily invalid block caching to reduce repeated network requests +- Added caching to the proposervm's inner block parsing + +### [Coreth](https://medium.com/avalancheavax/apricot-phase-6-native-asset-call-deprecation-a7b7a77b850a) + +- Reduced the log level of `BAD BLOCK`s from `ERROR` to `DEBUG` +- Deprecated Native Asset Call + +## [v1.8.2](https://github.com/ava-labs/avalanchego/releases/tag/v1.8.2) + +Please upgrade your node as soon as possible. + +The changes in `v1.8.x` go into effect at 4 PM EDT on September 6th, 2022 on both Fuji and Mainnet. You should upgrade your node before the changes go into effect, otherwise they may experience loss of uptime. + +The supported plugin version is `16`. + +### [Coreth](https://medium.com/avalancheavax/apricot-phase-6-native-asset-call-deprecation-a7b7a77b850a) + +- Fixed live-lock in bootstrapping, after performing state-sync, by properly reporting `database.ErrNotFound` in `GetBlockIDAtHeight` rather than a formatted error +- Increased the log level of `BAD BLOCK`s from `DEBUG` to `ERROR` +- Fixed typo in Chain Config `String` function + +## [v1.8.1](https://github.com/ava-labs/avalanchego/releases/tag/v1.8.1) + +Please upgrade your node as soon as possible. + +The changes in `v1.8.x` go into effect at 4 PM EDT on September 6th, 2022 on both Fuji and Mainnet. You should upgrade your node before the changes go into effect, otherwise they may experience loss of uptime. + +The supported plugin version is `16`. + +### Miscellaneous + +- Reduced the severity of not quickly connecting to bootstrap nodes from `FATAL` to `WARN` + +### [Coreth](https://medium.com/avalancheavax/apricot-phase-6-native-asset-call-deprecation-a7b7a77b850a) + +- Reduced the log level of `BAD BLOCK`s from `ERROR` to `DEBUG` +- Added Apricot Phase6 to Chain Config `String` function + +## [v1.8.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.8.0) + +This is a mandatory security upgrade. Please upgrade your node **as soon as possible.** + +The changes in the upgrade go into effect at **4 PM EDT on September 6th, 2022** on both Fuji and Mainnet. You should upgrade your node before the changes go into effect, otherwise they may experience loss of uptime. + +You may see some extraneous ERROR logs ("BAD BLOCK") on your node after upgrading. These may continue until the Apricot Phase 6 activation (at 4 PM EDT on September 6th). + +The supported plugin version is `16`. + +### PlatformVM APIs + +- Fixed `GetBlock` API when requesting the encoding as `json` +- Changed the json key in `AddSubnetValidatorTx`s from `subnet` to `subnetID` +- Added multiple asset support to `getBalance` +- Updated `PermissionlessValidator`s returned from `getCurrentValidators` and `getPendingValidators` to include `validationRewardOwner` and `delegationRewardOwner` +- Deprecated `rewardOwner` in `PermissionlessValidator`s returned from `getCurrentValidators` and `getPendingValidators` +- Added `subnetID` argument to `getCurrentSupply` +- Added multiple asset support to `getStake` +- Added `subnetID` argument to `getMinStake` + +### PlatformVM Structures + +- Renamed existing blocks + - `ProposalBlock` -> `ApricotProposalBlock` + - `AbortBlock` -> `ApricotAbortBlock` + - `CommitBlock` -> `ApricotCommitBlock` + - `StandardBlock` -> `ApricotStandardBlock` + - `AtomicBlock` -> `ApricotAtomicBlock` +- Added new block types **to be enabled in a future release** + - `BlueberryProposalBlock` + - Introduces a `Time` field and an unused `Txs` field before the remaining `ApricotProposalBlock` fields + - `BlueberryAbortBlock` + - Introduces a `Time` field before the remaining `ApricotAbortBlock` fields + - `BlueberryCommitBlock` + - Introduces a `Time` field before the remaining `ApricotCommitBlock` fields + - `BlueberryStandardBlock` + - Introduces a `Time` field before the remaining `ApricotStandardBlock` fields +- Added new transaction types **to be enabled in a future release** + - `RemoveSubnetValidatorTx` + - Can be included into `BlueberryStandardBlock`s + - Allows a subnet owner to remove a validator from their subnet + - `TransformSubnetTx` + - Can be included into `BlueberryStandardBlock`s + - Allows a subnet owner to convert their subnet into a permissionless subnet + - `AddPermissionlessValidatorTx` + - Can be included into `BlueberryStandardBlock`s + - Adds a new validator to the requested permissionless subnet + - `AddPermissionlessDelegatorTx` + - Can be included into `BlueberryStandardBlock`s + - Adds a new delegator to the requested permissionless validator on the requested subnet + +### PlatformVM Block Building + +- Fixed race in `AdvanceTimeTx` creation to avoid unnecessary block construction +- Added `block_formation_logic.md` to describe how blocks are created +- Refactored `BlockBuilder` into `ApricotBlockBuilder` +- Added `BlueberryBlockBuilder` +- Added `OptionBlock` builder visitor +- Refactored `Mempool` issuance and removal logic to use transaction visitors + +### PlatformVM Block Execution + +- Added support for executing `AddValidatorTx`, `AddDelegatorTx`, and `AddSubnetValidatorTx` inside of a `BlueberryStandardBlock` +- Refactored time advancement into a standard state modification structure +- Refactored `ProposalTxExecutor` to abstract state diff creation +- Standardized upgrade checking rules +- Refactored subnet authorization checking + +### Wallet + +- Added support for new transaction types in the P-chain wallet +- Fixed fee amounts used in the Primary Network wallet to reduce unnecessary fee burning + +### Networking + +- Defined `p2p.proto` to be used for future network messages +- Added `--network-tls-key-log-file-unsafe` to support inspecting p2p messages +- Added `avalanche_network_accept_failed` metrics to track networking `Accept` errors + +### Miscellaneous + +- Removed reserved fields from proto files and renumbered the existing fields +- Added generic dynamically resized ring buffer +- Updated gRPC version to `v1.49.0` to fix non-deterministic errors reported in the `rpcchainvm` +- Removed `--signature-verification-enabled` flag +- Removed dead code + - `ids.QueueSet` + - `timer.Repeater` + - `timer.NewStagedTimer` + - `timer.TimedMeter` + +### [Coreth](https://medium.com/avalancheavax/apricot-phase-6-native-asset-call-deprecation-a7b7a77b850a) + +- Incorrectly deprecated Native Asset Call +- Migrated to go-ethereum v1.10.23 +- Added API to fetch Chain Config + ## [v1.7.18](https://github.com/ava-labs/avalanchego/releases/tag/v1.7.18) This version is backwards compatible to [v1.7.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.7.0). It is optional, but encouraged. The supported plugin version is `15`. diff --git a/avalanchego/api/admin/client.go b/avalanchego/api/admin/client.go index 38a9fcd3..c6bda757 100644 --- a/avalanchego/api/admin/client.go +++ b/avalanchego/api/admin/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package admin diff --git a/avalanchego/api/admin/client_test.go b/avalanchego/api/admin/client_test.go index 6d661774..7119249b 100644 --- a/avalanchego/api/admin/client_test.go +++ b/avalanchego/api/admin/client_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package admin diff --git a/avalanchego/api/admin/service.go b/avalanchego/api/admin/service.go index 5d6f5594..72043369 100644 --- a/avalanchego/api/admin/service.go +++ b/avalanchego/api/admin/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package admin diff --git a/avalanchego/api/admin/service_test.go b/avalanchego/api/admin/service_test.go index 86da816a..ce7a4e80 100644 --- a/avalanchego/api/admin/service_test.go +++ b/avalanchego/api/admin/service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package admin diff --git a/avalanchego/api/auth/auth.go b/avalanchego/api/auth/auth.go index 9ebdb04e..3b10674a 100644 --- a/avalanchego/api/auth/auth.go +++ b/avalanchego/api/auth/auth.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package auth diff --git a/avalanchego/api/auth/auth_test.go b/avalanchego/api/auth/auth_test.go index a4457d17..46124c71 100644 --- a/avalanchego/api/auth/auth_test.go +++ b/avalanchego/api/auth/auth_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package auth diff --git a/avalanchego/api/auth/claims.go b/avalanchego/api/auth/claims.go index c093764f..444583bb 100644 --- a/avalanchego/api/auth/claims.go +++ b/avalanchego/api/auth/claims.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package auth diff --git a/avalanchego/api/auth/response.go b/avalanchego/api/auth/response.go index ba33d5df..94592070 100644 --- a/avalanchego/api/auth/response.go +++ b/avalanchego/api/auth/response.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package auth diff --git a/avalanchego/api/auth/service.go b/avalanchego/api/auth/service.go index ba3c43a6..2eebbf83 100644 --- a/avalanchego/api/auth/service.go +++ b/avalanchego/api/auth/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package auth diff --git a/avalanchego/api/common_args_responses.go b/avalanchego/api/common_args_responses.go index 688b39e7..6b6c2764 100644 --- a/avalanchego/api/common_args_responses.go +++ b/avalanchego/api/common_args_responses.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package api diff --git a/avalanchego/api/health/checker.go b/avalanchego/api/health/checker.go index 3430974c..64252e0f 100644 --- a/avalanchego/api/health/checker.go +++ b/avalanchego/api/health/checker.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/client.go b/avalanchego/api/health/client.go index b6271cca..f2a81f20 100644 --- a/avalanchego/api/health/client.go +++ b/avalanchego/api/health/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/client_test.go b/avalanchego/api/health/client_test.go index a9d3d11a..42ec539a 100644 --- a/avalanchego/api/health/client_test.go +++ b/avalanchego/api/health/client_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/handler.go b/avalanchego/api/health/handler.go index 577392d8..ef4d3322 100644 --- a/avalanchego/api/health/handler.go +++ b/avalanchego/api/health/handler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/health.go b/avalanchego/api/health/health.go index 4b77089e..712fcd55 100644 --- a/avalanchego/api/health/health.go +++ b/avalanchego/api/health/health.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/health_test.go b/avalanchego/api/health/health_test.go index 20bba68e..34a3c317 100644 --- a/avalanchego/api/health/health_test.go +++ b/avalanchego/api/health/health_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/metrics.go b/avalanchego/api/health/metrics.go index 9cf06e41..1d4948a5 100644 --- a/avalanchego/api/health/metrics.go +++ b/avalanchego/api/health/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/result.go b/avalanchego/api/health/result.go index 24e89681..8fc3e1b1 100644 --- a/avalanchego/api/health/result.go +++ b/avalanchego/api/health/result.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/service.go b/avalanchego/api/health/service.go index 6c17f399..64780c55 100644 --- a/avalanchego/api/health/service.go +++ b/avalanchego/api/health/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/service_test.go b/avalanchego/api/health/service_test.go index f0159d9d..ff384067 100644 --- a/avalanchego/api/health/service_test.go +++ b/avalanchego/api/health/service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/health/worker.go b/avalanchego/api/health/worker.go index 00e8db92..cb165b11 100644 --- a/avalanchego/api/health/worker.go +++ b/avalanchego/api/health/worker.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package health diff --git a/avalanchego/api/info/client.go b/avalanchego/api/info/client.go index 480e2c16..07305ad2 100644 --- a/avalanchego/api/info/client.go +++ b/avalanchego/api/info/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package info @@ -8,6 +8,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/rpc" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" ) var _ Client = &client{} @@ -15,7 +16,7 @@ var _ Client = &client{} // Client interface for an Info API Client type Client interface { GetNodeVersion(context.Context, ...rpc.Option) (*GetNodeVersionReply, error) - GetNodeID(context.Context, ...rpc.Option) (ids.NodeID, error) + GetNodeID(context.Context, ...rpc.Option) (ids.NodeID, *signer.ProofOfPossession, error) GetNodeIP(context.Context, ...rpc.Option) (string, error) GetNetworkID(context.Context, ...rpc.Option) (uint32, error) GetNetworkName(context.Context, ...rpc.Option) (string, error) @@ -46,10 +47,10 @@ func (c *client) GetNodeVersion(ctx context.Context, options ...rpc.Option) (*Ge return res, err } -func (c *client) GetNodeID(ctx context.Context, options ...rpc.Option) (ids.NodeID, error) { +func (c *client) GetNodeID(ctx context.Context, options ...rpc.Option) (ids.NodeID, *signer.ProofOfPossession, error) { res := &GetNodeIDReply{} err := c.requester.SendRequest(ctx, "getNodeID", struct{}{}, res, options...) - return res.NodeID, err + return res.NodeID, res.NodePOP, err } func (c *client) GetNodeIP(ctx context.Context, options ...rpc.Option) (string, error) { diff --git a/avalanchego/api/info/service.go b/avalanchego/api/info/service.go index db2f3029..59d2e574 100644 --- a/avalanchego/api/info/service.go +++ b/avalanchego/api/info/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package info @@ -23,6 +23,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" ) var ( @@ -43,14 +44,20 @@ type Info struct { } type Parameters struct { - Version *version.Application - NodeID ids.NodeID - NetworkID uint32 - TxFee uint64 - CreateAssetTxFee uint64 - CreateSubnetTxFee uint64 - CreateBlockchainTxFee uint64 - VMManager vms.Manager + Version *version.Application + NodeID ids.NodeID + NodePOP *signer.ProofOfPossession + NetworkID uint32 + TxFee uint64 + CreateAssetTxFee uint64 + CreateSubnetTxFee uint64 + TransformSubnetTxFee uint64 + CreateBlockchainTxFee uint64 + AddPrimaryNetworkValidatorFee uint64 + AddPrimaryNetworkDelegatorFee uint64 + AddSubnetValidatorFee uint64 + AddSubnetDelegatorFee uint64 + VMManager vms.Manager } // NewService returns a new admin API service @@ -109,7 +116,8 @@ func (service *Info) GetNodeVersion(_ *http.Request, _ *struct{}, reply *GetNode // GetNodeIDReply are the results from calling GetNodeID type GetNodeIDReply struct { - NodeID ids.NodeID `json:"nodeID"` + NodeID ids.NodeID `json:"nodeID"` + NodePOP *signer.ProofOfPossession `json:"nodePOP"` } // GetNodeID returns the node ID of this node @@ -117,6 +125,7 @@ func (service *Info) GetNodeID(_ *http.Request, _ *struct{}, reply *GetNodeIDRep service.log.Debug("Info: GetNodeID called") reply.NodeID = service.NodeID + reply.NodePOP = service.NodePOP return nil } @@ -277,10 +286,15 @@ func (service *Info) Uptime(_ *http.Request, _ *struct{}, reply *UptimeResponse) type GetTxFeeResponse struct { TxFee json.Uint64 `json:"txFee"` // TODO: remove [CreationTxFee] after enough time for dependencies to update - CreationTxFee json.Uint64 `json:"creationTxFee"` - CreateAssetTxFee json.Uint64 `json:"createAssetTxFee"` - CreateSubnetTxFee json.Uint64 `json:"createSubnetTxFee"` - CreateBlockchainTxFee json.Uint64 `json:"createBlockchainTxFee"` + CreationTxFee json.Uint64 `json:"creationTxFee"` + CreateAssetTxFee json.Uint64 `json:"createAssetTxFee"` + CreateSubnetTxFee json.Uint64 `json:"createSubnetTxFee"` + TransformSubnetTxFee json.Uint64 `json:"transformSubnetTxFee"` + CreateBlockchainTxFee json.Uint64 `json:"createBlockchainTxFee"` + AddPrimaryNetworkValidatorFee json.Uint64 `json:"addPrimaryNetworkValidatorFee"` + AddPrimaryNetworkDelegatorFee json.Uint64 `json:"addPrimaryNetworkDelegatorFee"` + AddSubnetValidatorFee json.Uint64 `json:"addSubnetValidatorFee"` + AddSubnetDelegatorFee json.Uint64 `json:"addSubnetDelegatorFee"` } // GetTxFee returns the transaction fee in nAVAX. @@ -289,7 +303,12 @@ func (service *Info) GetTxFee(_ *http.Request, args *struct{}, reply *GetTxFeeRe reply.CreationTxFee = json.Uint64(service.CreateAssetTxFee) reply.CreateAssetTxFee = json.Uint64(service.CreateAssetTxFee) reply.CreateSubnetTxFee = json.Uint64(service.CreateSubnetTxFee) + reply.TransformSubnetTxFee = json.Uint64(service.TransformSubnetTxFee) reply.CreateBlockchainTxFee = json.Uint64(service.CreateBlockchainTxFee) + reply.AddPrimaryNetworkValidatorFee = json.Uint64(service.AddPrimaryNetworkValidatorFee) + reply.AddPrimaryNetworkDelegatorFee = json.Uint64(service.AddPrimaryNetworkDelegatorFee) + reply.AddSubnetValidatorFee = json.Uint64(service.AddSubnetValidatorFee) + reply.AddSubnetDelegatorFee = json.Uint64(service.AddSubnetDelegatorFee) return nil } diff --git a/avalanchego/api/info/service_test.go b/avalanchego/api/info/service_test.go index 0c5ca552..1448e80b 100644 --- a/avalanchego/api/info/service_test.go +++ b/avalanchego/api/info/service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package info diff --git a/avalanchego/api/ipcs/client.go b/avalanchego/api/ipcs/client.go index 8f3d9754..154ccae4 100644 --- a/avalanchego/api/ipcs/client.go +++ b/avalanchego/api/ipcs/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ipcs diff --git a/avalanchego/api/ipcs/service.go b/avalanchego/api/ipcs/service.go index 8501c2e8..3ad5f426 100644 --- a/avalanchego/api/ipcs/service.go +++ b/avalanchego/api/ipcs/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ipcs diff --git a/avalanchego/api/keystore/blockchain_keystore.go b/avalanchego/api/keystore/blockchain_keystore.go index 10190e75..03f0bccf 100644 --- a/avalanchego/api/keystore/blockchain_keystore.go +++ b/avalanchego/api/keystore/blockchain_keystore.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/api/keystore/client.go b/avalanchego/api/keystore/client.go index b36652af..e170d16d 100644 --- a/avalanchego/api/keystore/client.go +++ b/avalanchego/api/keystore/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/api/keystore/codec.go b/avalanchego/api/keystore/codec.go index c5ba972d..f079c58d 100644 --- a/avalanchego/api/keystore/codec.go +++ b/avalanchego/api/keystore/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/api/keystore/gkeystore/keystore_client.go b/avalanchego/api/keystore/gkeystore/keystore_client.go index 6b9a384c..7902b73e 100644 --- a/avalanchego/api/keystore/gkeystore/keystore_client.go +++ b/avalanchego/api/keystore/gkeystore/keystore_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gkeystore diff --git a/avalanchego/api/keystore/gkeystore/keystore_server.go b/avalanchego/api/keystore/gkeystore/keystore_server.go index 26d1dcee..66e214e6 100644 --- a/avalanchego/api/keystore/gkeystore/keystore_server.go +++ b/avalanchego/api/keystore/gkeystore/keystore_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gkeystore diff --git a/avalanchego/api/keystore/keystore.go b/avalanchego/api/keystore/keystore.go index 57fdc51d..dbdc6425 100644 --- a/avalanchego/api/keystore/keystore.go +++ b/avalanchego/api/keystore/keystore.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/api/keystore/service.go b/avalanchego/api/keystore/service.go index da9d4c1a..955af65b 100644 --- a/avalanchego/api/keystore/service.go +++ b/avalanchego/api/keystore/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/api/keystore/service_test.go b/avalanchego/api/keystore/service_test.go index 083aae27..b208d1e7 100644 --- a/avalanchego/api/keystore/service_test.go +++ b/avalanchego/api/keystore/service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/api/metrics/gatherer_test.go b/avalanchego/api/metrics/gatherer_test.go index fd1440bb..28349d98 100644 --- a/avalanchego/api/metrics/gatherer_test.go +++ b/avalanchego/api/metrics/gatherer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/api/metrics/multi_gatherer.go b/avalanchego/api/metrics/multi_gatherer.go index 5997f796..a9c9c58e 100644 --- a/avalanchego/api/metrics/multi_gatherer.go +++ b/avalanchego/api/metrics/multi_gatherer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/api/metrics/multi_gatherer_test.go b/avalanchego/api/metrics/multi_gatherer_test.go index 8dab9e38..6ee29740 100644 --- a/avalanchego/api/metrics/multi_gatherer_test.go +++ b/avalanchego/api/metrics/multi_gatherer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/api/metrics/optional_gatherer.go b/avalanchego/api/metrics/optional_gatherer.go index 76c3694c..2355257a 100644 --- a/avalanchego/api/metrics/optional_gatherer.go +++ b/avalanchego/api/metrics/optional_gatherer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/api/metrics/optional_gatherer_test.go b/avalanchego/api/metrics/optional_gatherer_test.go index 94305d44..1d45b7d5 100644 --- a/avalanchego/api/metrics/optional_gatherer_test.go +++ b/avalanchego/api/metrics/optional_gatherer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/api/server/middleware_handler.go b/avalanchego/api/server/middleware_handler.go index fa744e38..7abaf5f1 100644 --- a/avalanchego/api/server/middleware_handler.go +++ b/avalanchego/api/server/middleware_handler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package server diff --git a/avalanchego/api/server/router.go b/avalanchego/api/server/router.go index c54c881c..90474281 100644 --- a/avalanchego/api/server/router.go +++ b/avalanchego/api/server/router.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package server diff --git a/avalanchego/api/server/router_test.go b/avalanchego/api/server/router_test.go index 48400728..8b898320 100644 --- a/avalanchego/api/server/router_test.go +++ b/avalanchego/api/server/router_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package server diff --git a/avalanchego/api/server/server.go b/avalanchego/api/server/server.go index 44ca1bdb..fb697c57 100644 --- a/avalanchego/api/server/server.go +++ b/avalanchego/api/server/server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package server diff --git a/avalanchego/api/server/wrapper.go b/avalanchego/api/server/wrapper.go index f4840318..47f092d1 100644 --- a/avalanchego/api/server/wrapper.go +++ b/avalanchego/api/server/wrapper.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package server diff --git a/avalanchego/app/app.go b/avalanchego/app/app.go index ecddcfb4..90e28bac 100644 --- a/avalanchego/app/app.go +++ b/avalanchego/app/app.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package app diff --git a/avalanchego/app/plugin/plugin.go b/avalanchego/app/plugin/plugin.go index 493e91e1..3706169b 100644 --- a/avalanchego/app/plugin/plugin.go +++ b/avalanchego/app/plugin/plugin.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package plugin diff --git a/avalanchego/app/plugin/plugin_client.go b/avalanchego/app/plugin/plugin_client.go index 37f25a38..f1d5e232 100644 --- a/avalanchego/app/plugin/plugin_client.go +++ b/avalanchego/app/plugin/plugin_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package plugin diff --git a/avalanchego/app/plugin/plugin_server.go b/avalanchego/app/plugin/plugin_server.go index ed209ec8..64d15f3c 100644 --- a/avalanchego/app/plugin/plugin_server.go +++ b/avalanchego/app/plugin/plugin_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package plugin diff --git a/avalanchego/app/plugin/process.go b/avalanchego/app/plugin/process.go index 59a52d77..e49cd007 100644 --- a/avalanchego/app/plugin/process.go +++ b/avalanchego/app/plugin/process.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package plugin diff --git a/avalanchego/app/process/process.go b/avalanchego/app/process/process.go index 69b52fd9..ee78d20f 100644 --- a/avalanchego/app/process/process.go +++ b/avalanchego/app/process/process.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package process @@ -85,17 +85,6 @@ func (p *process) Start() error { ) } - // Check if transaction signatures should be checked - if !p.config.EnableCrypto { - // TODO: actually disable crypto verification - log.Warn("transaction signatures are not being checked") - } - - // Track if assertions should be executed - if p.config.LoggingConfig.Assertions { - log.Debug("assertions are enabled. This may slow down execution") - } - // TODO move this to config // SupportsNAT() for NoRouter is false. // Which means we tried to perform a NAT activity but we were not successful. diff --git a/avalanchego/app/runner/config.go b/avalanchego/app/runner/config.go index b7e8caeb..14197ff2 100644 --- a/avalanchego/app/runner/config.go +++ b/avalanchego/app/runner/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package runner diff --git a/avalanchego/app/runner/runner.go b/avalanchego/app/runner/runner.go index 077e5361..b2589eb1 100644 --- a/avalanchego/app/runner/runner.go +++ b/avalanchego/app/runner/runner.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package runner diff --git a/avalanchego/cache/cache.go b/avalanchego/cache/cache.go index 017fc697..f69dca5e 100644 --- a/avalanchego/cache/cache.go +++ b/avalanchego/cache/cache.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cache diff --git a/avalanchego/cache/lru_cache.go b/avalanchego/cache/lru_cache.go index ce3819ae..460da6d2 100644 --- a/avalanchego/cache/lru_cache.go +++ b/avalanchego/cache/lru_cache.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cache diff --git a/avalanchego/cache/lru_cache_benchmark_test.go b/avalanchego/cache/lru_cache_benchmark_test.go index aabddda7..20c45e62 100644 --- a/avalanchego/cache/lru_cache_benchmark_test.go +++ b/avalanchego/cache/lru_cache_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cache diff --git a/avalanchego/cache/lru_cache_test.go b/avalanchego/cache/lru_cache_test.go index 6de5b028..0228c42a 100644 --- a/avalanchego/cache/lru_cache_test.go +++ b/avalanchego/cache/lru_cache_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cache diff --git a/avalanchego/cache/metercacher/cache.go b/avalanchego/cache/metercacher/cache.go index b372455c..c8b809c3 100644 --- a/avalanchego/cache/metercacher/cache.go +++ b/avalanchego/cache/metercacher/cache.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metercacher diff --git a/avalanchego/cache/metercacher/cache_test.go b/avalanchego/cache/metercacher/cache_test.go index cd081e6c..64c547e1 100644 --- a/avalanchego/cache/metercacher/cache_test.go +++ b/avalanchego/cache/metercacher/cache_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metercacher diff --git a/avalanchego/cache/metercacher/metrics.go b/avalanchego/cache/metercacher/metrics.go index 177fd8c2..6353615e 100644 --- a/avalanchego/cache/metercacher/metrics.go +++ b/avalanchego/cache/metercacher/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metercacher diff --git a/avalanchego/cache/test_cacher.go b/avalanchego/cache/test_cacher.go index 5e13c6d7..32c8e795 100644 --- a/avalanchego/cache/test_cacher.go +++ b/avalanchego/cache/test_cacher.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cache diff --git a/avalanchego/cache/unique_cache.go b/avalanchego/cache/unique_cache.go index 61a5c735..59a26ffe 100644 --- a/avalanchego/cache/unique_cache.go +++ b/avalanchego/cache/unique_cache.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cache diff --git a/avalanchego/cache/unique_cache_test.go b/avalanchego/cache/unique_cache_test.go index 950dbff6..790f6c38 100644 --- a/avalanchego/cache/unique_cache_test.go +++ b/avalanchego/cache/unique_cache_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cache diff --git a/avalanchego/chains/atomic/codec.go b/avalanchego/chains/atomic/codec.go index 6195d69b..0138d2f7 100644 --- a/avalanchego/chains/atomic/codec.go +++ b/avalanchego/chains/atomic/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/gsharedmemory/filtered_batch.go b/avalanchego/chains/atomic/gsharedmemory/filtered_batch.go index 3f3bd519..e04564c4 100644 --- a/avalanchego/chains/atomic/gsharedmemory/filtered_batch.go +++ b/avalanchego/chains/atomic/gsharedmemory/filtered_batch.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gsharedmemory diff --git a/avalanchego/chains/atomic/gsharedmemory/shared_memory_client.go b/avalanchego/chains/atomic/gsharedmemory/shared_memory_client.go index 8fe14b34..40e419fa 100644 --- a/avalanchego/chains/atomic/gsharedmemory/shared_memory_client.go +++ b/avalanchego/chains/atomic/gsharedmemory/shared_memory_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gsharedmemory diff --git a/avalanchego/chains/atomic/gsharedmemory/shared_memory_server.go b/avalanchego/chains/atomic/gsharedmemory/shared_memory_server.go index 2dccea9a..0725e7e6 100644 --- a/avalanchego/chains/atomic/gsharedmemory/shared_memory_server.go +++ b/avalanchego/chains/atomic/gsharedmemory/shared_memory_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gsharedmemory diff --git a/avalanchego/chains/atomic/gsharedmemory/shared_memory_test.go b/avalanchego/chains/atomic/gsharedmemory/shared_memory_test.go index 76c763f8..99aed9d8 100644 --- a/avalanchego/chains/atomic/gsharedmemory/shared_memory_test.go +++ b/avalanchego/chains/atomic/gsharedmemory/shared_memory_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gsharedmemory diff --git a/avalanchego/chains/atomic/memory.go b/avalanchego/chains/atomic/memory.go index fe4a909b..daa85920 100644 --- a/avalanchego/chains/atomic/memory.go +++ b/avalanchego/chains/atomic/memory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/memory_test.go b/avalanchego/chains/atomic/memory_test.go index 8a9b1a1f..2dc337d9 100644 --- a/avalanchego/chains/atomic/memory_test.go +++ b/avalanchego/chains/atomic/memory_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/prefixes.go b/avalanchego/chains/atomic/prefixes.go index 471827e7..95b2f5a7 100644 --- a/avalanchego/chains/atomic/prefixes.go +++ b/avalanchego/chains/atomic/prefixes.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/shared_memory.go b/avalanchego/chains/atomic/shared_memory.go index eef2cafa..e77f8c10 100644 --- a/avalanchego/chains/atomic/shared_memory.go +++ b/avalanchego/chains/atomic/shared_memory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/shared_memory_test.go b/avalanchego/chains/atomic/shared_memory_test.go index fec6d7e6..afe9b434 100644 --- a/avalanchego/chains/atomic/shared_memory_test.go +++ b/avalanchego/chains/atomic/shared_memory_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/state.go b/avalanchego/chains/atomic/state.go index 289a926d..b2279bb1 100644 --- a/avalanchego/chains/atomic/state.go +++ b/avalanchego/chains/atomic/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/test_shared_memory.go b/avalanchego/chains/atomic/test_shared_memory.go index 60c6f8b3..1d8bac5f 100644 --- a/avalanchego/chains/atomic/test_shared_memory.go +++ b/avalanchego/chains/atomic/test_shared_memory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/atomic/writer.go b/avalanchego/chains/atomic/writer.go index 5cdf7368..00b18d2a 100644 --- a/avalanchego/chains/atomic/writer.go +++ b/avalanchego/chains/atomic/writer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package atomic diff --git a/avalanchego/chains/manager.go b/avalanchego/chains/manager.go index 7c157001..e80ee733 100644 --- a/avalanchego/chains/manager.go +++ b/avalanchego/chains/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chains @@ -72,10 +72,10 @@ var ( // Manager manages the chains running on this node. // It can: -// * Create a chain -// * Add a registrant. When a chain is created, each registrant calls +// - Create a chain +// - Add a registrant. When a chain is created, each registrant calls // RegisterChain with the new chain as the argument. -// * Manage the aliases of chains +// - Manage the aliases of chains type Manager interface { ids.Aliaser @@ -148,6 +148,7 @@ type ManagerConfig struct { ConsensusAcceptorGroup snow.AcceptorGroup DBManager dbManager.Manager MsgCreator message.Creator // message creator, shared with network + MsgCreatorWithProto message.Creator // message creator using protobufs, shared with network Router router.Router // Routes incoming messages to the appropriate chain Net network.Network // Sends consensus messages to other validators ConsensusParams avcon.Parameters // The consensus parameters (alpha, beta, etc.) for new chains @@ -187,6 +188,7 @@ type ManagerConfig struct { ApricotPhase4Time time.Time ApricotPhase4MinPChainHeight uint64 + BanffTime time.Time // Tracks CPU/disk usage caused by each peer. ResourceTracker timetracker.ResourceTracker @@ -319,7 +321,12 @@ func (m *manager) ForceCreateChain(chainParams ChainParameters) { m.chainsLock.Unlock() // Associate the newly created chain with its default alias - m.Log.AssertNoError(m.Alias(chainParams.ID, chainParams.ID.String())) + if err := m.Alias(chainParams.ID, chainParams.ID.String()); err != nil { + m.Log.Error("failed to alias the new chain with itself", + zap.Stringer("chainID", chainParams.ID), + zap.Error(err), + ) + } // Notify those that registered to be notified when a new chain is created m.notifyRegistrants(chain.Name, chain.Engine) @@ -567,6 +574,8 @@ func (m *manager) createAvalancheChain( sender, err := sender.New( ctx, m.MsgCreator, + m.MsgCreatorWithProto, + m.BanffTime, m.Net, m.ManagerConfig.Router, m.TimeoutManager, @@ -753,6 +762,8 @@ func (m *manager) createSnowmanChain( sender, err := sender.New( ctx, m.MsgCreator, + m.MsgCreatorWithProto, + m.BanffTime, m.Net, m.ManagerConfig.Router, m.TimeoutManager, @@ -795,8 +806,12 @@ func (m *manager) createSnowmanChain( return nil, fmt.Errorf("error while fetching chain config: %w", err) } - // enable ProposerVM on this VM - vm = proposervm.New(vm, m.ApricotPhase4Time, m.ApricotPhase4MinPChainHeight) + vm = proposervm.New( + vm, + m.ApricotPhase4Time, + m.ApricotPhase4MinPChainHeight, + m.BanffTime, + ) if m.MeterVMEnabled { vm = metervm.NewBlockVM(vm) diff --git a/avalanchego/chains/mock_manager.go b/avalanchego/chains/mock_manager.go index d4bd5bd0..ea4295db 100644 --- a/avalanchego/chains/mock_manager.go +++ b/avalanchego/chains/mock_manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chains diff --git a/avalanchego/chains/registrant.go b/avalanchego/chains/registrant.go index 22eade2b..1c9fc0a5 100644 --- a/avalanchego/chains/registrant.go +++ b/avalanchego/chains/registrant.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chains diff --git a/avalanchego/chains/subnet.go b/avalanchego/chains/subnet.go index 6e345078..2299448f 100644 --- a/avalanchego/chains/subnet.go +++ b/avalanchego/chains/subnet.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chains diff --git a/avalanchego/chains/subnet_test.go b/avalanchego/chains/subnet_test.go index 17974bfd..1e1574a1 100644 --- a/avalanchego/chains/subnet_test.go +++ b/avalanchego/chains/subnet_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chains diff --git a/avalanchego/codec/codec.go b/avalanchego/codec/codec.go index f79aa93f..2343e345 100644 --- a/avalanchego/codec/codec.go +++ b/avalanchego/codec/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package codec diff --git a/avalanchego/codec/general_codec.go b/avalanchego/codec/general_codec.go index 2cffd5f6..8281b15e 100644 --- a/avalanchego/codec/general_codec.go +++ b/avalanchego/codec/general_codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package codec diff --git a/avalanchego/codec/hierarchycodec/codec.go b/avalanchego/codec/hierarchycodec/codec.go index f7670d2e..481cd6bb 100644 --- a/avalanchego/codec/hierarchycodec/codec.go +++ b/avalanchego/codec/hierarchycodec/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package hierarchycodec diff --git a/avalanchego/codec/hierarchycodec/codec_test.go b/avalanchego/codec/hierarchycodec/codec_test.go index 270e10e3..7fcbee6b 100644 --- a/avalanchego/codec/hierarchycodec/codec_test.go +++ b/avalanchego/codec/hierarchycodec/codec_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package hierarchycodec diff --git a/avalanchego/codec/linearcodec/codec.go b/avalanchego/codec/linearcodec/codec.go index a92d436b..1a94c78b 100644 --- a/avalanchego/codec/linearcodec/codec.go +++ b/avalanchego/codec/linearcodec/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linearcodec diff --git a/avalanchego/codec/linearcodec/codec_test.go b/avalanchego/codec/linearcodec/codec_test.go index 063c4640..feed6b44 100644 --- a/avalanchego/codec/linearcodec/codec_test.go +++ b/avalanchego/codec/linearcodec/codec_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linearcodec diff --git a/avalanchego/codec/manager.go b/avalanchego/codec/manager.go index 198f71d8..e9a91323 100644 --- a/avalanchego/codec/manager.go +++ b/avalanchego/codec/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package codec diff --git a/avalanchego/codec/reflectcodec/struct_fielder.go b/avalanchego/codec/reflectcodec/struct_fielder.go index efbfe552..49e5d860 100644 --- a/avalanchego/codec/reflectcodec/struct_fielder.go +++ b/avalanchego/codec/reflectcodec/struct_fielder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package reflectcodec diff --git a/avalanchego/codec/reflectcodec/type_codec.go b/avalanchego/codec/reflectcodec/type_codec.go index aa6db86e..710060aa 100644 --- a/avalanchego/codec/reflectcodec/type_codec.go +++ b/avalanchego/codec/reflectcodec/type_codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package reflectcodec @@ -19,6 +19,8 @@ const ( ) var ( + ErrMaxMarshalSliceLimitExceeded = errors.New("maximum marshal slice limit exceeded") + errMarshalNil = errors.New("can't marshal nil pointer or interface") errUnmarshalNil = errors.New("can't unmarshal nil") errNeedPointer = errors.New("argument to unmarshal must be a pointer") @@ -138,7 +140,8 @@ func (c *genericCodec) marshal(value reflect.Value, p *wrappers.Packer, maxSlice case reflect.Slice: numElts := value.Len() // # elements in the slice/array. 0 if this slice is nil. if uint32(numElts) > maxSliceLen { - return fmt.Errorf("slice length, %d, exceeds maximum length, %d", + return fmt.Errorf("%w; slice length, %d, exceeds maximum length, %d", + ErrMaxMarshalSliceLimitExceeded, numElts, maxSliceLen) } @@ -166,7 +169,7 @@ func (c *genericCodec) marshal(value reflect.Value, p *wrappers.Packer, maxSlice return p.Err } if uint32(numElts) > c.maxSliceLen { - return fmt.Errorf("array length, %d, exceeds maximum length, %d", numElts, c.maxSliceLen) + return fmt.Errorf("%w; array length, %d, exceeds maximum length, %d", ErrMaxMarshalSliceLimitExceeded, numElts, c.maxSliceLen) } for i := 0; i < numElts; i++ { // Process each element in the array if err := c.marshal(value.Index(i), p, c.maxSliceLen); err != nil { @@ -277,12 +280,14 @@ func (c *genericCodec) unmarshal(p *wrappers.Packer, value reflect.Value, maxSli return fmt.Errorf("couldn't unmarshal slice: %w", p.Err) } if numElts32 > maxSliceLen { - return fmt.Errorf("array length, %d, exceeds maximum length, %d", + return fmt.Errorf("%w; array length, %d, exceeds maximum length, %d", + ErrMaxMarshalSliceLimitExceeded, numElts32, maxSliceLen) } if numElts32 > math.MaxInt32 { - return fmt.Errorf("array length, %d, exceeds maximum length, %d", + return fmt.Errorf("%w; array length, %d, exceeds maximum length, %d", + ErrMaxMarshalSliceLimitExceeded, numElts32, math.MaxInt32) } diff --git a/avalanchego/codec/registry.go b/avalanchego/codec/registry.go index aea9186b..b61acf2b 100644 --- a/avalanchego/codec/registry.go +++ b/avalanchego/codec/registry.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package codec diff --git a/avalanchego/codec/test_codec.go b/avalanchego/codec/test_codec.go index b2eda114..f0a40c63 100644 --- a/avalanchego/codec/test_codec.go +++ b/avalanchego/codec/test_codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package codec diff --git a/avalanchego/config/config.go b/avalanchego/config/config.go index 482909c2..5ea87ea5 100644 --- a/avalanchego/config/config.go +++ b/avalanchego/config/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package config @@ -9,6 +9,7 @@ import ( "encoding/json" "errors" "fmt" + "io/fs" "math" "net" "os" @@ -36,10 +37,12 @@ import ( "github.com/ava-labs/avalanchego/snow/networking/tracker" "github.com/ava-labs/avalanchego/staking" "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/dynamicip" "github.com/ava-labs/avalanchego/utils/ips" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/password" + "github.com/ava-labs/avalanchego/utils/perms" "github.com/ava-labs/avalanchego/utils/profiler" "github.com/ava-labs/avalanchego/utils/storage" "github.com/ava-labs/avalanchego/utils/timer" @@ -72,8 +75,8 @@ var ( errStakeMaxConsumptionBelowMin = errors.New("stake max consumption can't be less than min stake consumption") errStakeMintingPeriodBelowMin = errors.New("stake minting period can't be less than max stake duration") errCannotWhitelistPrimaryNetwork = errors.New("cannot whitelist primary network") - errStakingKeyContentUnset = fmt.Errorf("%s key not set but %s set", StakingKeyContentKey, StakingCertContentKey) - errStakingCertContentUnset = fmt.Errorf("%s key set but %s not set", StakingKeyContentKey, StakingCertContentKey) + errStakingKeyContentUnset = fmt.Errorf("%s key not set but %s set", StakingTLSKeyContentKey, StakingCertContentKey) + errStakingCertContentUnset = fmt.Errorf("%s key set but %s not set", StakingTLSKeyContentKey, StakingCertContentKey) ) func GetRunnerConfig(v *viper.Viper) (runner.Config, error) { @@ -383,6 +386,8 @@ func getNetworkConfig(v *viper.Viper, halflife time.Duration) (network.Config, e ConnectionTimeout: v.GetDuration(OutboundConnectionTimeout), }, + TLSKeyLogFile: v.GetString(NetworkTLSKeyLogFileKey), + TimeoutConfig: network.TimeoutConfig{ PingPongTimeout: v.GetDuration(NetworkPingTimeoutKey), ReadHandshakeTimeout: v.GetDuration(NetworkReadHandshakeTimeoutKey), @@ -649,7 +654,7 @@ func getProfilerConfig(v *viper.Viper) (profiler.Config, error) { } func getStakingTLSCertFromFlag(v *viper.Viper) (tls.Certificate, error) { - stakingKeyRawContent := v.GetString(StakingKeyContentKey) + stakingKeyRawContent := v.GetString(StakingTLSKeyContentKey) stakingKeyContent, err := base64.StdEncoding.DecodeString(stakingKeyRawContent) if err != nil { return tls.Certificate{}, fmt.Errorf("unable to decode base64 content: %w", err) @@ -671,11 +676,11 @@ func getStakingTLSCertFromFlag(v *viper.Viper) (tls.Certificate, error) { func getStakingTLSCertFromFile(v *viper.Viper) (tls.Certificate, error) { // Parse the staking key/cert paths and expand environment variables - stakingKeyPath := GetExpandedArg(v, StakingKeyPathKey) + stakingKeyPath := GetExpandedArg(v, StakingTLSKeyPathKey) stakingCertPath := GetExpandedArg(v, StakingCertPathKey) // If staking key/cert locations are specified but not found, error - if v.IsSet(StakingKeyPathKey) || v.IsSet(StakingCertPathKey) { + if v.IsSet(StakingTLSKeyPathKey) || v.IsSet(StakingCertPathKey) { if _, err := os.Stat(stakingKeyPath); os.IsNotExist(err) { return tls.Certificate{}, fmt.Errorf("couldn't find staking key at %s", stakingKeyPath) } else if _, err := os.Stat(stakingCertPath); os.IsNotExist(err) { @@ -707,23 +712,83 @@ func getStakingTLSCert(v *viper.Viper) (tls.Certificate, error) { } switch { - case v.IsSet(StakingKeyContentKey) && !v.IsSet(StakingCertContentKey): + case v.IsSet(StakingTLSKeyContentKey) && !v.IsSet(StakingCertContentKey): return tls.Certificate{}, errStakingCertContentUnset - case !v.IsSet(StakingKeyContentKey) && v.IsSet(StakingCertContentKey): + case !v.IsSet(StakingTLSKeyContentKey) && v.IsSet(StakingCertContentKey): return tls.Certificate{}, errStakingKeyContentUnset - case v.IsSet(StakingKeyContentKey) && v.IsSet(StakingCertContentKey): + case v.IsSet(StakingTLSKeyContentKey) && v.IsSet(StakingCertContentKey): return getStakingTLSCertFromFlag(v) default: return getStakingTLSCertFromFile(v) } } +func getStakingSigner(v *viper.Viper) (*bls.SecretKey, error) { + if v.GetBool(StakingEphemeralSignerEnabledKey) { + key, err := bls.NewSecretKey() + if err != nil { + return nil, fmt.Errorf("couldn't generate ephemeral signing key: %w", err) + } + return key, nil + } + + if v.IsSet(StakingSignerKeyContentKey) { + signerKeyRawContent := v.GetString(StakingSignerKeyContentKey) + signerKeyContent, err := base64.StdEncoding.DecodeString(signerKeyRawContent) + if err != nil { + return nil, fmt.Errorf("unable to decode base64 content: %w", err) + } + key, err := bls.SecretKeyFromBytes(signerKeyContent) + if err != nil { + return nil, fmt.Errorf("couldn't parse signing key: %w", err) + } + return key, nil + } + + signingKeyPath := GetExpandedArg(v, StakingSignerKeyPathKey) + _, err := os.Stat(signingKeyPath) + if !errors.Is(err, fs.ErrNotExist) { + signingKeyBytes, err := os.ReadFile(signingKeyPath) + if err != nil { + return nil, err + } + key, err := bls.SecretKeyFromBytes(signingKeyBytes) + if err != nil { + return nil, fmt.Errorf("couldn't parse signing key: %w", err) + } + return key, nil + } + + if v.IsSet(StakingSignerKeyPathKey) { + return nil, errors.New("missing staking signing key file") + } + + key, err := bls.NewSecretKey() + if err != nil { + return nil, fmt.Errorf("couldn't generate new signing key: %w", err) + } + + if err := os.MkdirAll(filepath.Dir(signingKeyPath), perms.ReadWriteExecute); err != nil { + return nil, fmt.Errorf("couldn't create path for signing key at %s: %w", signingKeyPath, err) + } + + keyBytes := bls.SecretKeyToBytes(key) + if err := os.WriteFile(signingKeyPath, keyBytes, perms.ReadWrite); err != nil { + return nil, fmt.Errorf("couldn't write new signing key to %s: %w", signingKeyPath, err) + } + if err := os.Chmod(signingKeyPath, perms.ReadOnly); err != nil { + return nil, fmt.Errorf("couldn't restrict permissions on new signing key at %s: %w", signingKeyPath, err) + } + return key, nil +} + func getStakingConfig(v *viper.Viper, networkID uint32) (node.StakingConfig, error) { config := node.StakingConfig{ EnableStaking: v.GetBool(StakingEnabledKey), DisabledStakingWeight: v.GetUint64(StakingDisabledWeightKey), - StakingKeyPath: GetExpandedArg(v, StakingKeyPathKey), + StakingKeyPath: GetExpandedArg(v, StakingTLSKeyPathKey), StakingCertPath: GetExpandedArg(v, StakingCertPathKey), + StakingSignerPath: GetExpandedArg(v, StakingSignerKeyPathKey), } if !config.EnableStaking && config.DisabledStakingWeight == 0 { return node.StakingConfig{}, errInvalidStakerWeights @@ -738,6 +803,10 @@ func getStakingConfig(v *viper.Viper, networkID uint32) (node.StakingConfig, err if err != nil { return node.StakingConfig{}, err } + config.StakingSigningKey, err = getStakingSigner(v) + if err != nil { + return node.StakingConfig{}, err + } if networkID != constants.MainnetID { config.UptimeRequirement = v.GetFloat64(UptimeRequirementKey) config.MinValidatorStake = v.GetUint64(MinValidatorStakeKey) @@ -777,10 +846,15 @@ func getStakingConfig(v *viper.Viper, networkID uint32) (node.StakingConfig, err func getTxFeeConfig(v *viper.Viper, networkID uint32) genesis.TxFeeConfig { if networkID != constants.MainnetID { return genesis.TxFeeConfig{ - TxFee: v.GetUint64(TxFeeKey), - CreateAssetTxFee: v.GetUint64(CreateAssetTxFeeKey), - CreateSubnetTxFee: v.GetUint64(CreateSubnetTxFeeKey), - CreateBlockchainTxFee: v.GetUint64(CreateBlockchainTxFeeKey), + TxFee: v.GetUint64(TxFeeKey), + CreateAssetTxFee: v.GetUint64(CreateAssetTxFeeKey), + CreateSubnetTxFee: v.GetUint64(CreateSubnetTxFeeKey), + TransformSubnetTxFee: v.GetUint64(TransformSubnetTxFeeKey), + CreateBlockchainTxFee: v.GetUint64(CreateBlockchainTxFeeKey), + AddPrimaryNetworkValidatorFee: v.GetUint64(AddPrimaryNetworkValidatorFeeKey), + AddPrimaryNetworkDelegatorFee: v.GetUint64(AddPrimaryNetworkDelegatorFeeKey), + AddSubnetValidatorFee: v.GetUint64(AddSubnetValidatorFeeKey), + AddSubnetDelegatorFee: v.GetUint64(AddSubnetDelegatorFeeKey), } } return genesis.GetTxFeeConfig(networkID) @@ -1265,12 +1339,6 @@ func GetNodeConfig(v *viper.Viper, buildDir string) (node.Config, error) { return node.Config{}, fmt.Errorf("unable to load genesis file: %w", err) } - // Assertions - nodeConfig.EnableAssertions = v.GetBool(AssertionsEnabledKey) - - // Crypto - nodeConfig.EnableCrypto = v.GetBool(SignatureVerificationEnabledKey) - // StateSync Configs nodeConfig.StateSyncConfig, err = getStateSyncConfig(v) if err != nil { diff --git a/avalanchego/config/config_test.go b/avalanchego/config/config_test.go index 8e7364aa..098da2e1 100644 --- a/avalanchego/config/config_test.go +++ b/avalanchego/config/config_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package config diff --git a/avalanchego/config/flags.go b/avalanchego/config/flags.go index 5fbfd3ca..39a8dcdb 100644 --- a/avalanchego/config/flags.go +++ b/avalanchego/config/flags.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package config @@ -34,18 +34,19 @@ const ( var ( // [defaultUnexpandedDataDir] will be expanded when reading the flags - defaultDataDir = filepath.Join("$HOME", ".avalanchego") - defaultDBDir = filepath.Join(defaultUnexpandedDataDir, "db") - defaultLogDir = filepath.Join(defaultUnexpandedDataDir, "logs") - defaultProfileDir = filepath.Join(defaultUnexpandedDataDir, "profiles") - defaultStakingPath = filepath.Join(defaultUnexpandedDataDir, "staking") - defaultStakingKeyPath = filepath.Join(defaultStakingPath, "staker.key") - defaultStakingCertPath = filepath.Join(defaultStakingPath, "staker.crt") - defaultConfigDir = filepath.Join(defaultUnexpandedDataDir, "configs") - defaultChainConfigDir = filepath.Join(defaultConfigDir, "chains") - defaultVMConfigDir = filepath.Join(defaultConfigDir, "vms") - defaultVMAliasFilePath = filepath.Join(defaultVMConfigDir, "aliases.json") - defaultSubnetConfigDir = filepath.Join(defaultConfigDir, "subnets") + defaultDataDir = filepath.Join("$HOME", ".avalanchego") + defaultDBDir = filepath.Join(defaultUnexpandedDataDir, "db") + defaultLogDir = filepath.Join(defaultUnexpandedDataDir, "logs") + defaultProfileDir = filepath.Join(defaultUnexpandedDataDir, "profiles") + defaultStakingPath = filepath.Join(defaultUnexpandedDataDir, "staking") + defaultStakingTLSKeyPath = filepath.Join(defaultStakingPath, "staker.key") + defaultStakingCertPath = filepath.Join(defaultStakingPath, "staker.crt") + defaultStakingSignerKeyPath = filepath.Join(defaultStakingPath, "signer.key") + defaultConfigDir = filepath.Join(defaultUnexpandedDataDir, "configs") + defaultChainConfigDir = filepath.Join(defaultConfigDir, "chains") + defaultVMConfigDir = filepath.Join(defaultConfigDir, "vms") + defaultVMAliasFilePath = filepath.Join(defaultVMConfigDir, "aliases.json") + defaultSubnetConfigDir = filepath.Join(defaultConfigDir, "subnets") // Places to look for the build directory defaultBuildDirs = []string{} @@ -102,7 +103,12 @@ func addNodeFlags(fs *flag.FlagSet) { fs.Uint64(TxFeeKey, genesis.LocalParams.TxFee, "Transaction fee, in nAVAX") fs.Uint64(CreateAssetTxFeeKey, genesis.LocalParams.CreateAssetTxFee, "Transaction fee, in nAVAX, for transactions that create new assets") fs.Uint64(CreateSubnetTxFeeKey, genesis.LocalParams.CreateSubnetTxFee, "Transaction fee, in nAVAX, for transactions that create new subnets") + fs.Uint64(TransformSubnetTxFeeKey, genesis.LocalParams.TransformSubnetTxFee, "Transaction fee, in nAVAX, for transactions that transform subnets") fs.Uint64(CreateBlockchainTxFeeKey, genesis.LocalParams.CreateBlockchainTxFee, "Transaction fee, in nAVAX, for transactions that create new blockchains") + fs.Uint64(AddPrimaryNetworkValidatorFeeKey, genesis.LocalParams.AddPrimaryNetworkValidatorFee, "Transaction fee, in nAVAX, for transactions that add new primary network validators") + fs.Uint64(AddPrimaryNetworkDelegatorFeeKey, genesis.LocalParams.AddPrimaryNetworkDelegatorFee, "Transaction fee, in nAVAX, for transactions that add new primary network delegators") + fs.Uint64(AddSubnetValidatorFeeKey, genesis.LocalParams.AddSubnetValidatorFee, "Transaction fee, in nAVAX, for transactions that add new subnet validators") + fs.Uint64(AddSubnetDelegatorFeeKey, genesis.LocalParams.AddSubnetDelegatorFee, "Transaction fee, in nAVAX, for transactions that add new subnet delegators") // Database fs.String(DBTypeKey, leveldb.Name, fmt.Sprintf("Database type to use. Should be one of {%s, %s}", leveldb.Name, memdb.Name)) @@ -121,12 +127,6 @@ func addNodeFlags(fs *flag.FlagSet) { fs.Bool(LogRotaterCompressEnabledKey, false, "Enables the compression of rotated log files through gzip.") fs.Bool(LogDisableDisplayPluginLogsKey, false, "Disables displaying plugin logs in stdout.") - // Assertions - fs.Bool(AssertionsEnabledKey, true, "Turn on assertion execution") - - // Signature Verification - fs.Bool(SignatureVerificationEnabledKey, true, "Turn on signature verification") - // Peer List Gossip gossipHelpMsg := fmt.Sprintf( "Gossip [%s] validator IPs to [%s] validators, [%s] non-validators, and [%s] validating or non-validating peers every [%s]", @@ -138,8 +138,8 @@ func addNodeFlags(fs *flag.FlagSet) { ) fs.Uint(NetworkPeerListNumValidatorIPsKey, 15, gossipHelpMsg) fs.Uint(NetworkPeerListValidatorGossipSizeKey, 20, gossipHelpMsg) - fs.Uint(NetworkPeerListNonValidatorGossipSizeKey, 10, gossipHelpMsg) - fs.Uint(NetworkPeerListPeersGossipSizeKey, 0, gossipHelpMsg) + fs.Uint(NetworkPeerListNonValidatorGossipSizeKey, 0, gossipHelpMsg) + fs.Uint(NetworkPeerListPeersGossipSizeKey, 10, gossipHelpMsg) fs.Duration(NetworkPeerListGossipFreqKey, time.Minute, gossipHelpMsg) // Public IP Resolution @@ -173,6 +173,8 @@ func addNodeFlags(fs *flag.FlagSet) { fs.Uint(NetworkPeerReadBufferSizeKey, 8*units.KiB, "Size, in bytes, of the buffer that we read peer messages into (there is one buffer per peer)") fs.Uint(NetworkPeerWriteBufferSizeKey, 8*units.KiB, "Size, in bytes, of the buffer that we write peer messages into (there is one buffer per peer)") + fs.String(NetworkTLSKeyLogFileKey, "", "TLS key log file path. Should only be specified for debugging") + // Benchlist fs.Int(BenchlistFailThresholdKey, 10, "Number of consecutive failed queries before benchlisting a node") fs.Duration(BenchlistDurationKey, 15*time.Minute, "Max amount of time a peer is benchlisted after surpassing the threshold") @@ -248,11 +250,15 @@ func addNodeFlags(fs *flag.FlagSet) { // Staking fs.Uint(StakingPortKey, DefaultStakingPort, "Port of the consensus server") fs.Bool(StakingEnabledKey, true, "Enable staking. If enabled, Network TLS is required") - fs.Bool(StakingEphemeralCertEnabledKey, false, "If true, the node uses an ephemeral staking key and certificate, and has an ephemeral node ID") - fs.String(StakingKeyPathKey, defaultStakingKeyPath, fmt.Sprintf("Path to the TLS private key for staking. Ignored if %s is specified", StakingKeyContentKey)) - fs.String(StakingKeyContentKey, "", "Specifies base64 encoded TLS private key for staking") + fs.Bool(StakingEphemeralCertEnabledKey, false, "If true, the node uses an ephemeral staking TLS key and certificate, and has an ephemeral node ID") + fs.String(StakingTLSKeyPathKey, defaultStakingTLSKeyPath, fmt.Sprintf("Path to the TLS private key for staking. Ignored if %s is specified", StakingTLSKeyContentKey)) + fs.String(StakingTLSKeyContentKey, "", "Specifies base64 encoded TLS private key for staking") fs.String(StakingCertPathKey, defaultStakingCertPath, fmt.Sprintf("Path to the TLS certificate for staking. Ignored if %s is specified", StakingCertContentKey)) fs.String(StakingCertContentKey, "", "Specifies base64 encoded TLS certificate for staking") + fs.Bool(StakingEphemeralSignerEnabledKey, false, "If true, the node uses an ephemeral staking signer key") + fs.String(StakingSignerKeyPathKey, defaultStakingSignerKeyPath, fmt.Sprintf("Path to the signer private key for staking. Ignored if %s is specified", StakingSignerKeyContentKey)) + fs.String(StakingSignerKeyContentKey, "", "Specifies base64 encoded signer private key for staking") + fs.Uint64(StakingDisabledWeightKey, 100, "Weight to provide to each peer when staking is disabled") // Uptime Requirement fs.Float64(UptimeRequirementKey, genesis.LocalParams.UptimeRequirement, "Fraction of time a validator must be online to receive rewards") @@ -284,7 +290,7 @@ func addNodeFlags(fs *flag.FlagSet) { fs.String(BootstrapIDsKey, "", "Comma separated list of bootstrap peer ids to connect to. Example: NodeID-JR4dVmy6ffUGAKCBDkyCbeZbyHQBeDsET,NodeID-8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z") fs.Bool(RetryBootstrapKey, true, "Specifies whether bootstrap should be retried") fs.Int(RetryBootstrapWarnFrequencyKey, 50, "Specifies how many times bootstrap should be retried before warning the operator") - fs.Duration(BootstrapBeaconConnectionTimeoutKey, time.Minute, "Timeout when attempting to connect to bootstrapping beacons") + fs.Duration(BootstrapBeaconConnectionTimeoutKey, time.Minute, "Timeout before emitting a warn log when connecting to bootstrapping beacons") fs.Duration(BootstrapMaxTimeGetAncestorsKey, 50*time.Millisecond, "Max Time to spend fetching a container and its ancestors when responding to a GetAncestors") fs.Uint(BootstrapAncestorsMaxContainersSentKey, 2000, "Max number of containers in an Ancestors message sent by this node") fs.Uint(BootstrapAncestorsMaxContainersReceivedKey, 2000, "This node reads at most this many containers from an incoming Ancestors message") diff --git a/avalanchego/config/keys.go b/avalanchego/config/keys.go index 818f6ee7..745c78ba 100644 --- a/avalanchego/config/keys.go +++ b/avalanchego/config/keys.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package config @@ -16,7 +16,12 @@ const ( TxFeeKey = "tx-fee" CreateAssetTxFeeKey = "create-asset-tx-fee" CreateSubnetTxFeeKey = "create-subnet-tx-fee" + TransformSubnetTxFeeKey = "transform-subnet-tx-fee" CreateBlockchainTxFeeKey = "create-blockchain-tx-fee" + AddPrimaryNetworkValidatorFeeKey = "add-primary-network-validator-fee" + AddPrimaryNetworkDelegatorFeeKey = "add-primary-network-delegator-fee" + AddSubnetValidatorFeeKey = "add-subnet-validator-fee" + AddSubnetDelegatorFeeKey = "add-subnet-delegator-fee" UptimeRequirementKey = "uptime-requirement" MinValidatorStakeKey = "min-validator-stake" MaxValidatorStakeKey = "max-validator-stake" @@ -28,8 +33,6 @@ const ( StakeMinConsumptionRateKey = "stake-min-consumption-rate" StakeMintingPeriodKey = "stake-minting-period" StakeSupplyCapKey = "stake-supply-cap" - AssertionsEnabledKey = "assertions-enabled" - SignatureVerificationEnabledKey = "signature-verification-enabled" DBTypeKey = "db-type" DBPathKey = "db-dir" DBConfigFileKey = "db-config-file" @@ -63,10 +66,13 @@ const ( StakingPortKey = "staking-port" StakingEnabledKey = "staking-enabled" StakingEphemeralCertEnabledKey = "staking-ephemeral-cert-enabled" - StakingKeyPathKey = "staking-tls-key-file" - StakingKeyContentKey = "staking-tls-key-file-content" + StakingTLSKeyPathKey = "staking-tls-key-file" + StakingTLSKeyContentKey = "staking-tls-key-file-content" StakingCertPathKey = "staking-tls-cert-file" StakingCertContentKey = "staking-tls-cert-file-content" + StakingEphemeralSignerEnabledKey = "staking-ephemeral-signer-enabled" + StakingSignerKeyPathKey = "staking-signer-key-file" + StakingSignerKeyContentKey = "staking-signer-key-file-content" StakingDisabledWeightKey = "staking-disabled-weight" NetworkInitialTimeoutKey = "network-initial-timeout" NetworkMinimumTimeoutKey = "network-minimum-timeout" @@ -96,6 +102,7 @@ const ( NetworkRequireValidatorToConnectKey = "network-require-validator-to-connect" NetworkPeerReadBufferSizeKey = "network-peer-read-buffer-size" NetworkPeerWriteBufferSizeKey = "network-peer-write-buffer-size" + NetworkTLSKeyLogFileKey = "network-tls-key-log-file-unsafe" BenchlistFailThresholdKey = "benchlist-fail-threshold" BenchlistDurationKey = "benchlist-duration" BenchlistMinFailingDurationKey = "benchlist-min-failing-duration" @@ -153,7 +160,7 @@ const ( RetryBootstrapWarnFrequencyKey = "bootstrap-retry-warn-frequency" PluginModeKey = "plugin-mode-enabled" BootstrapBeaconConnectionTimeoutKey = "bootstrap-beacon-connection-timeout" - BootstrapMaxTimeGetAncestorsKey = "boostrap-max-time-get-ancestors" + BootstrapMaxTimeGetAncestorsKey = "bootstrap-max-time-get-ancestors" BootstrapAncestorsMaxContainersSentKey = "bootstrap-ancestors-max-containers-sent" BootstrapAncestorsMaxContainersReceivedKey = "bootstrap-ancestors-max-containers-received" ChainConfigDirKey = "chain-config-dir" diff --git a/avalanchego/config/pflags.go b/avalanchego/config/pflags.go index 85ef0f07..791c9d68 100644 --- a/avalanchego/config/pflags.go +++ b/avalanchego/config/pflags.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package config diff --git a/avalanchego/config/viper.go b/avalanchego/config/viper.go index bee73a40..f198e73b 100644 --- a/avalanchego/config/viper.go +++ b/avalanchego/config/viper.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package config diff --git a/avalanchego/database/batch.go b/avalanchego/database/batch.go index b62e4fcf..573446e8 100644 --- a/avalanchego/database/batch.go +++ b/avalanchego/database/batch.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // For ease of implementation, our database's interface matches Ethereum's diff --git a/avalanchego/database/benchmark_database.go b/avalanchego/database/benchmark_database.go index aadbd644..48745168 100644 --- a/avalanchego/database/benchmark_database.go +++ b/avalanchego/database/benchmark_database.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package database diff --git a/avalanchego/database/common.go b/avalanchego/database/common.go index 0c66d636..4de692d7 100644 --- a/avalanchego/database/common.go +++ b/avalanchego/database/common.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package database diff --git a/avalanchego/database/corruptabledb/db.go b/avalanchego/database/corruptabledb/db.go index d403a56e..d8f04024 100644 --- a/avalanchego/database/corruptabledb/db.go +++ b/avalanchego/database/corruptabledb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package corruptabledb diff --git a/avalanchego/database/corruptabledb/db_test.go b/avalanchego/database/corruptabledb/db_test.go index a7a50708..b9143cbd 100644 --- a/avalanchego/database/corruptabledb/db_test.go +++ b/avalanchego/database/corruptabledb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package corruptabledb diff --git a/avalanchego/database/database.go b/avalanchego/database/database.go index 3a45022a..c762bc3d 100644 --- a/avalanchego/database/database.go +++ b/avalanchego/database/database.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // For ease of implementation, our database's interface matches Ethereum's diff --git a/avalanchego/database/encdb/db.go b/avalanchego/database/encdb/db.go index de0e8c56..d0db1ae1 100644 --- a/avalanchego/database/encdb/db.go +++ b/avalanchego/database/encdb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package encdb diff --git a/avalanchego/database/encdb/db_test.go b/avalanchego/database/encdb/db_test.go index 2ca8b3b8..571a79df 100644 --- a/avalanchego/database/encdb/db_test.go +++ b/avalanchego/database/encdb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package encdb diff --git a/avalanchego/database/errors.go b/avalanchego/database/errors.go index 85de53a5..ee77dbbb 100644 --- a/avalanchego/database/errors.go +++ b/avalanchego/database/errors.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package database diff --git a/avalanchego/database/helpers.go b/avalanchego/database/helpers.go index 04d92de6..e1281b53 100644 --- a/avalanchego/database/helpers.go +++ b/avalanchego/database/helpers.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package database diff --git a/avalanchego/database/iterator.go b/avalanchego/database/iterator.go index 0f84461d..04402023 100644 --- a/avalanchego/database/iterator.go +++ b/avalanchego/database/iterator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // For ease of implementation, our database's interface matches Ethereum's diff --git a/avalanchego/database/leveldb/db.go b/avalanchego/database/leveldb/db.go index fa8f5e36..91681a66 100644 --- a/avalanchego/database/leveldb/db.go +++ b/avalanchego/database/leveldb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package leveldb diff --git a/avalanchego/database/leveldb/db_test.go b/avalanchego/database/leveldb/db_test.go index 2a86e34e..764ad806 100644 --- a/avalanchego/database/leveldb/db_test.go +++ b/avalanchego/database/leveldb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package leveldb diff --git a/avalanchego/database/leveldb/metrics.go b/avalanchego/database/leveldb/metrics.go index 3ae67ea2..6d19c661 100644 --- a/avalanchego/database/leveldb/metrics.go +++ b/avalanchego/database/leveldb/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package leveldb diff --git a/avalanchego/database/linkeddb/codec.go b/avalanchego/database/linkeddb/codec.go index b70f9b88..2c92d1a6 100644 --- a/avalanchego/database/linkeddb/codec.go +++ b/avalanchego/database/linkeddb/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linkeddb diff --git a/avalanchego/database/linkeddb/linkeddb.go b/avalanchego/database/linkeddb/linkeddb.go index 42f89b92..2879527b 100644 --- a/avalanchego/database/linkeddb/linkeddb.go +++ b/avalanchego/database/linkeddb/linkeddb.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linkeddb diff --git a/avalanchego/database/linkeddb/linkeddb_test.go b/avalanchego/database/linkeddb/linkeddb_test.go index 6330b7e0..d120d693 100644 --- a/avalanchego/database/linkeddb/linkeddb_test.go +++ b/avalanchego/database/linkeddb/linkeddb_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linkeddb diff --git a/avalanchego/database/manager/manager.go b/avalanchego/database/manager/manager.go index 6f402e97..c2459052 100644 --- a/avalanchego/database/manager/manager.go +++ b/avalanchego/database/manager/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package manager diff --git a/avalanchego/database/manager/manager_test.go b/avalanchego/database/manager/manager_test.go index 33c686ed..e4f3843e 100644 --- a/avalanchego/database/manager/manager_test.go +++ b/avalanchego/database/manager/manager_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package manager diff --git a/avalanchego/database/manager/versioned_database.go b/avalanchego/database/manager/versioned_database.go index 7e512acd..23a36a5b 100644 --- a/avalanchego/database/manager/versioned_database.go +++ b/avalanchego/database/manager/versioned_database.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package manager diff --git a/avalanchego/database/memdb/db.go b/avalanchego/database/memdb/db.go index 1b3a92db..62ed1441 100644 --- a/avalanchego/database/memdb/db.go +++ b/avalanchego/database/memdb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package memdb diff --git a/avalanchego/database/memdb/db_test.go b/avalanchego/database/memdb/db_test.go index d4cf5ce1..ed3279ce 100644 --- a/avalanchego/database/memdb/db_test.go +++ b/avalanchego/database/memdb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package memdb diff --git a/avalanchego/database/meterdb/db.go b/avalanchego/database/meterdb/db.go index 31eeccc5..f3d7d74e 100644 --- a/avalanchego/database/meterdb/db.go +++ b/avalanchego/database/meterdb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meterdb diff --git a/avalanchego/database/meterdb/db_test.go b/avalanchego/database/meterdb/db_test.go index 4d518f60..fc03fe19 100644 --- a/avalanchego/database/meterdb/db_test.go +++ b/avalanchego/database/meterdb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meterdb diff --git a/avalanchego/database/meterdb/metrics.go b/avalanchego/database/meterdb/metrics.go index 6d50dfeb..814396c3 100644 --- a/avalanchego/database/meterdb/metrics.go +++ b/avalanchego/database/meterdb/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meterdb diff --git a/avalanchego/database/mockdb/db.go b/avalanchego/database/mockdb/db.go index c776af81..17ce1bb7 100644 --- a/avalanchego/database/mockdb/db.go +++ b/avalanchego/database/mockdb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package mockdb diff --git a/avalanchego/database/mockdb/db_test.go b/avalanchego/database/mockdb/db_test.go index 0e92990c..9388a4e7 100644 --- a/avalanchego/database/mockdb/db_test.go +++ b/avalanchego/database/mockdb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package mockdb diff --git a/avalanchego/database/nodb/db.go b/avalanchego/database/nodb/db.go index ecedf1ab..24d71e83 100644 --- a/avalanchego/database/nodb/db.go +++ b/avalanchego/database/nodb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nodb diff --git a/avalanchego/database/prefixdb/db.go b/avalanchego/database/prefixdb/db.go index dee20059..65f9fa8c 100644 --- a/avalanchego/database/prefixdb/db.go +++ b/avalanchego/database/prefixdb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package prefixdb diff --git a/avalanchego/database/prefixdb/db_test.go b/avalanchego/database/prefixdb/db_test.go index 3bcf2c9d..39c73d87 100644 --- a/avalanchego/database/prefixdb/db_test.go +++ b/avalanchego/database/prefixdb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package prefixdb diff --git a/avalanchego/database/rpcdb/db_client.go b/avalanchego/database/rpcdb/db_client.go index 5589e418..f49380ac 100644 --- a/avalanchego/database/rpcdb/db_client.go +++ b/avalanchego/database/rpcdb/db_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcdb diff --git a/avalanchego/database/rpcdb/db_server.go b/avalanchego/database/rpcdb/db_server.go index 20b091f3..8eb1e99a 100644 --- a/avalanchego/database/rpcdb/db_server.go +++ b/avalanchego/database/rpcdb/db_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcdb diff --git a/avalanchego/database/rpcdb/db_test.go b/avalanchego/database/rpcdb/db_test.go index 917c7606..b16b13db 100644 --- a/avalanchego/database/rpcdb/db_test.go +++ b/avalanchego/database/rpcdb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcdb diff --git a/avalanchego/database/rpcdb/errors.go b/avalanchego/database/rpcdb/errors.go index 25aa05cf..b7d1bd52 100644 --- a/avalanchego/database/rpcdb/errors.go +++ b/avalanchego/database/rpcdb/errors.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcdb diff --git a/avalanchego/database/test_database.go b/avalanchego/database/test_database.go index d3bc86a5..ee57bc1e 100644 --- a/avalanchego/database/test_database.go +++ b/avalanchego/database/test_database.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package database diff --git a/avalanchego/database/versiondb/db.go b/avalanchego/database/versiondb/db.go index b71a2242..a9f5a3a3 100644 --- a/avalanchego/database/versiondb/db.go +++ b/avalanchego/database/versiondb/db.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package versiondb diff --git a/avalanchego/database/versiondb/db_test.go b/avalanchego/database/versiondb/db_test.go index c4ef4925..558b1f5b 100644 --- a/avalanchego/database/versiondb/db_test.go +++ b/avalanchego/database/versiondb/db_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package versiondb diff --git a/avalanchego/genesis/aliases.go b/avalanchego/genesis/aliases.go index 86a2cd50..bdcca296 100644 --- a/avalanchego/genesis/aliases.go +++ b/avalanchego/genesis/aliases.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis diff --git a/avalanchego/genesis/beacons.go b/avalanchego/genesis/beacons.go index 372746ed..e613b19c 100644 --- a/avalanchego/genesis/beacons.go +++ b/avalanchego/genesis/beacons.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis diff --git a/avalanchego/genesis/config.go b/avalanchego/genesis/config.go index 1738dbe5..99231732 100644 --- a/avalanchego/genesis/config.go +++ b/avalanchego/genesis/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis diff --git a/avalanchego/genesis/genesis.go b/avalanchego/genesis/genesis.go index d50f8bef..593282d4 100644 --- a/avalanchego/genesis/genesis.go +++ b/avalanchego/genesis/genesis.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis @@ -400,7 +400,7 @@ func FromConfig(config *Config) ([]byte, ids.ID, error) { delegationFee := json.Uint32(staker.DelegationFee) platformvmArgs.Validators = append(platformvmArgs.Validators, - api.PrimaryValidator{ + api.PermissionlessValidator{ Staker: api.Staker{ StartTime: json.Uint64(genesisTime.Unix()), EndTime: json.Uint64(endStakingTime.Unix()), diff --git a/avalanchego/genesis/genesis_local.go b/avalanchego/genesis/genesis_local.go index 6086efc0..ae66c0c4 100644 --- a/avalanchego/genesis/genesis_local.go +++ b/avalanchego/genesis/genesis_local.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis @@ -87,10 +87,15 @@ var ( // LocalParams are the params used for local networks LocalParams = Params{ TxFeeConfig: TxFeeConfig{ - TxFee: units.MilliAvax, - CreateAssetTxFee: units.MilliAvax, - CreateSubnetTxFee: 100 * units.MilliAvax, - CreateBlockchainTxFee: 100 * units.MilliAvax, + TxFee: units.MilliAvax, + CreateAssetTxFee: units.MilliAvax, + CreateSubnetTxFee: 100 * units.MilliAvax, + TransformSubnetTxFee: 100 * units.MilliAvax, + CreateBlockchainTxFee: 100 * units.MilliAvax, + AddPrimaryNetworkValidatorFee: 0, + AddPrimaryNetworkDelegatorFee: 0, + AddSubnetValidatorFee: units.MilliAvax, + AddSubnetDelegatorFee: units.MilliAvax, }, StakingConfig: StakingConfig{ UptimeRequirement: .8, // 80% diff --git a/avalanchego/genesis/genesis_localFlare.json b/avalanchego/genesis/genesis_localFlare.json index cd2a26f2..ff23af74 100644 --- a/avalanchego/genesis/genesis_localFlare.json +++ b/avalanchego/genesis/genesis_localFlare.json @@ -24,7 +24,7 @@ ] } ], - "startTime": 1685592000, + "startTime": 1717200000, "initialStakeDuration": 31536000, "initialStakeDurationOffset": 5400, "initialStakedFunds": [ @@ -39,4 +39,4 @@ ], "cChainGenesis": "{\"config\":{\"chainId\":162,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0100000000000000000000000000000000000000\",\"alloc\":{\"0x1000000000000000000000000000000000000001\":{\"balance\":\"0x0\",\"code\":\"0x608060405234801561001057600080fd5b50600436106101005760003560e01c8063c2f56d4c11610097578063ec7424a011610066578063ec7424a014610284578063f417c9d81461028c578063f5f59a4a14610294578063f64b6fda1461029c57610100565b8063c2f56d4c146101f0578063cfd1fdad14610216578063dd86215714610259578063eaebf6d31461026157610100565b80635f8c940d116100d35780635f8c940d1461018157806371c5ecb11461018957806371e24574146101a65780637ff6faa6146101cc57610100565b8063273c463b1461010557806329be4db21461012d5780633c70b3571461015c5780634b8a125f14610179575b600080fd5b61012b6004803603602081101561011b57600080fd5b50356001600160a01b031661030c565b005b61014a6004803603602081101561014357600080fd5b503561033b565b60408051918252519081900360200190f35b61014a6004803603602081101561017257600080fd5b503561049a565b61014a61059c565b61014a6105a4565b61014a6004803603602081101561019f57600080fd5b50356105a9565b61014a600480360360208110156101bc57600080fd5b50356001600160a01b03166105c1565b6101d46105d6565b604080516001600160a01b039092168252519081900360200190f35b6101d46004803603602081101561020657600080fd5b50356001600160a01b03166105dd565b6102456004803603608081101561022c57600080fd5b50803590602081013590604081013590606001356105f8565b604080519115158252519081900360200190f35b61014a6106d4565b61012b6004803603604081101561027757600080fd5b508035906020013561072c565b61014a6107d0565b61014a6107d6565b61014a6107dc565b61012b600480360360208110156102b257600080fd5b8101906020810181356401000000008111156102cd57600080fd5b8201836020820111156102df57600080fd5b8035906020019184600183028401116401000000008311171561030157600080fd5b5090925090506107e1565b33600090815260106020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b336000908152601060205260408120546001600160a01b03168061035c5750335b6001831161036957600080fd5b6001600160a01b03811660009081526011602052604090206009015460001984019081111561039757600080fd5b6001600160a01b03821660009081526011602052604081206003600019840106600381106103c157fe5b600390810291909101546001600160a01b0385166000908152601160205260409020909250908306600381106103f357fe5b60030201600101549350600060116000856001600160a01b03166001600160a01b031681526020019081526020016000206000016003848161043157fe5b066003811061043c57fe5b6003020160020154905084818560405160200180848152602001838152602001826001600160a01b03168152602001935050505060405160208183030381529060405280519060200120821461049157600080fd5b50505050919050565b6000600360125410156104e7576040805162461bcd60e51b815260206004820152601060248201526f746f74616c42756666657273203c203360801b604482015290519081900360640190fd5b600360125403821115610531576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd08199a5b985b1a5e9959609a1b604482015290519081900360640190fd5b611a408210806105475750601254611a42190182115b610582576040805162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b604482015290519081900360640190fd5b6013611a408306611a40811061059457fe5b015492915050565b636184740081565b600381565b601381611a4081106105ba57600080fd5b0154905081565b60116020526000908152604090206009015481565b620dead181565b6010602052600090815260409020546001600160a01b031681565b6000605a63618473ff19420104851461064d576040805162461bcd60e51b81526020600482015260126024820152713bb937b73390313ab33332b9273ab6b132b960711b604482015290519081900360640190fd5b336000818152601160208181526040808420600981018b905581516060810183528a81528084018a9052918201889052949093525290600387066003811061069157fe5b600302016000820151816000015560208201518160010155604082015181600201559050506012548511156106c8575060016106cc565b5060005b949350505050565b600060036012541015610721576040805162461bcd60e51b815260206004820152601060248201526f746f74616c42756666657273203c203360801b604482015290519081900360640190fd5b506012546002190190565b6003821161073957600080fd5b605a63618473ff19420104821461074f57600080fd5b601254821161075d57600080fd5b334114801561076e575041620dead1145b156107cc576012829055806013611a40600219850106611a40811061078f57fe5b01556040805182815290516002198401917f8ffd19aa79a62d0764e560d21b1245698310783be781d7d80b38233d4d7d288c919081900360200190a25b5050565b60125481565b611a4081565b605a81565b7f7cbc6812801238dea8eb58356bb62b95dbce8dc28498aa30e7d2c6873ed36cc73342848460405180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a1505056fea26469706673582212201835668a4aedf7b8dd6cb953219e7b126f41acff165aaa41023acd8f3dd8edf064736f6c63430007060033\"},\"0x1000000000000000000000000000000000000002\":{\"balance\":\"0x0\",\"code\":\"0x608060405234801561001057600080fd5b50600436106101f05760003560e01c8063870196b81161010f578063d48a38df116100a2578063e9de7d6011610071578063e9de7d6014610397578063ecdda0dd146103aa578063ed21b6e4146103bd578063f5a98383146103d4576101f0565b8063d48a38df1461035c578063dded1b4714610364578063e17f212e1461036c578063e371aef014610381576101f0565b8063aea36b53116100de578063aea36b5314610326578063b00c0b7614610339578063be0522e01461034c578063c9f960eb14610354576101f0565b8063870196b8146102f05780638be2fb86146103035780639d6a890f1461030b578063a6817ace1461031e576101f0565b806362da19a511610187578063689c499911610156578063689c4999146102ac57806372993615146102bf57806374e6310e146102c75780637fec8d38146102e8576101f0565b806362da19a514610270578063639031431461027857806363d4a53a1461028057806367fc402914610299576101f0565b80635267a15d116101c35780635267a15d146102385780635aa6e6751461024d5780635ff270791461025557806362354e0314610268576101f0565b806310663750146101f55780631d76dea1146102135780634f6a77b51461021b5780635042916c14610223575b600080fd5b6101fd6103dc565b60405161020a9190613397565b60405180910390f35b6101fd6103e2565b6101fd6103e8565b6102366102313660046130a6565b6103ee565b005b610240610435565b60405161020a919061317d565b61024061045a565b610236610263366004612ffe565b6104f0565b610240610845565b6101fd610850565b6101fd610856565b610288610860565b60405161020a959493929190613283565b6102366102a7366004612ffe565b61089b565b6102366102ba366004612f0e565b610983565b6101fd6109c0565b6102da6102d5366004612ffe565b6109c6565b60405161020a9291906133a0565b6101fd610a6c565b6102366102fe3660046130a6565b610acc565b6101fd610c08565b610236610319366004612e3c565b610c0e565b6101fd610c10565b610236610334366004612e3c565b610c16565b610236610347366004612e58565b610ca5565b610240610d50565b610240610d5f565b6101fd610d9b565b6101fd610da1565b610374610da7565b60405161020a9190613322565b610389610db7565b60405161020a929190613375565b6102366103a53660046130a6565b610dd8565b6102886103b83660046130be565b610f10565b6103c561126d565b60405161020a9392919061320e565b610236611432565b60055481565b60095481565b60085481565b600054600160b01b900460ff16806104105750600054600160a81b900460ff16155b156104275761041d6114ec565b600c819055610432565b610432600036611523565b50565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b60008054600160a81b900460ff1661047d576000546001600160a01b03166104ea565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b1580156104bd57600080fd5b505afa1580156104d1573d6000803e3d6000fd5b505050506040513d60208110156104e757600080fd5b50515b90505b90565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b15801561053457600080fd5b505afa158015610548573d6000803e3d6000fd5b505050506040513d602081101561055e57600080fd5b50516105a1576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b031981166000908152600160205260409020805461060d576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b8054421015610663576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106fd5780601f106106d2576101008083540402835291602001916106fd565b820191906000526020600020905b8154815290600101906020018083116106e057829003601f168201915b5050506001600160e01b03198616600090815260016020819052604082208281559495509092506107319150830182612ccb565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106107795780518252601f19909201916020918201910161075a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a161083f816116a6565b50505050565b60076001609c1b0181565b60045481565b60006104ea6116c3565b600b5460609081908190819060009061088a90600160c01b90046001600160401b03166001610f10565b945094509450945094509091929394565b6108a36116ce565b6001600160e01b0319811660009081526001602052604090205461090e576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b0319811660009081526001602081905260408220828155919061097e90830182612ccb565b505050565b600054600160b01b900460ff16806109a55750600054600160a81b900460ff16155b15610427576109b26114ec565b6109bb8161172d565b610432565b60075481565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f81018590048502860185019096528585529094919392909190830182828015610a625780601f10610a3757610100808354040283529160200191610a62565b820191906000526020600020905b815481529060010190602001808311610a4557829003601f168201915b5050505050905082565b6002546000906001600160a01b0316610ab157610ab1306040518060400160405280600e81526020016d696e666c6174696f6e207a65726f60901b81525060006119da565b60026001609c1b013314610ac457600080fd5b6104ea611b9e565b600054600160b01b900460ff1680610aee5750600054600160a81b900460ff16155b1561042757610afb6114ec565b600854610b0b90606e606461244f565b811115604051806040016040528060118152602001700dac2f040dad2dce840e8dede40d0d2ced607b1b81525090610b5f5760405162461bcd60e51b8152600401610b569190613362565b60405180910390fd5b5060408051808201909152601081526f6d6178206d696e74206973207a65726f60801b602082015281610ba55760405162461bcd60e51b8152600401610b569190613362565b5062015180600a54014211604051806040016040528060128152602001711d1a5b594819d85c081d1bdbc81cda1bdc9d60721b81525090610bf95760405162461bcd60e51b8152600401610b569190613362565b50600881905542600a55610432565b60035481565bfe5b600a5481565b600054600160b01b900460ff1680610c385750600054600160a81b900460ff16155b1561042757610c456114ec565b6000610c4f610435565b6001600160a01b0316146040518060400160405280600b81526020016a185b1c9958591e481cd95d60aa1b81525090610c9b5760405162461bcd60e51b8152600401610b569190613362565b506109bb8161255d565b610cad610435565b6001600160a01b0316336001600160a01b031614610d09576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b610d42610d3d83836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b815250612581565b61255d565b610d4c82826126ae565b5050565b6002546001600160a01b031681565b600f5460009060ff16610d8c57600f805460ff191660011790556000610d836128d6565b91506104ed9050565b610d9461045a565b90506104ed565b600c5481565b60065481565b600054600160a81b900460ff1681565b600b546001600160c01b03811690600160c01b90046001600160401b031682565b60025460408051808201909152600d81526c3737ba1034b7333630ba34b7b760991b602082015233916001600160a01b03168214610e295760405162461bcd60e51b8152600401610b569190613362565b5060085482111560405180604001604052806007815260200166746f6f2062696760c81b81525090610e6e5760405162461bcd60e51b8152600401610b569190613362565b5042610e786116c3565b10604051806040016040528060098152602001683a37b79037b33a32b760b91b81525090610eb95760405162461bcd60e51b8152600401610b569190613362565b508115610d4c5742600955600454610ed190836128f6565b6004556040517f4c4f1efc376f31abeb51b72c5f9ed81cf4016591312bb02337e58149dcfaaab490610f04908490613397565b60405180910390a15050565b606080606080600060148054905087106040518060400160405280601081526020016f0e6e8c2e4e840d2dcc8caf040d0d2ced60831b81525090610f675760405162461bcd60e51b8152600401610b569190613362565b506014546000908888011115610f8257601454889003610f84565b865b9050806001600160401b0381118015610f9c57600080fd5b50604051908082528060200260200182016040528015610fc6578160200160208202803683370190505b509550806001600160401b0381118015610fdf57600080fd5b50604051908082528060200260200182016040528015611009578160200160208202803683370190505b509450806001600160401b038111801561102257600080fd5b5060405190808252806020026020018201604052801561105657816020015b60608152602001906001900390816110415790505b509350806001600160401b038111801561106f57600080fd5b50604051908082528060200260200182016040528015611099578160200160208202803683370190505b50925060005b818110156112515760006014828b01815481106110b857fe5b6000918252602080832090910154808352601390915260409091205489519192506001600160c01b0316908990849081106110ef57fe5b6020026020010181815250506013600082815260200190815260200160002060000160189054906101000a90046001600160401b03166001600160401b031687838151811061113a57fe5b6020908102919091018101919091526000828152601382526040908190206002908101805483516001821615610100026000190190911692909204601f810185900485028301850190935282825290929091908301828280156111de5780601f106111b3576101008083540402835291602001916111de565b820191906000526020600020905b8154815290600101906020018083116111c157829003601f168201915b50505050508683815181106111ef57fe5b60200260200101819052506013600082815260200190815260200160002060010160009054906101000a90046001600160a01b031685838151811061123057fe5b6001600160a01b03909216602092830291909101909101525060010161109f565b5050600b549497939650919450926001600160c01b0316919050565b60105460609081908190806001600160401b038111801561128d57600080fd5b506040519080825280602002602001820160405280156112b7578160200160208202803683370190505b509350806001600160401b03811180156112d057600080fd5b506040519080825280602002602001820160405280156112fa578160200160208202803683370190505b509250806001600160401b038111801561131357600080fd5b5060405190808252806020026020018201604052801561133d578160200160208202803683370190505b50915060005b8181101561142b5760006010828154811061135a57fe5b9060005260206000200160009054906101000a90046001600160a01b031690508086838151811061138757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060116000826001600160a01b03166001600160a01b03168152602001908152602001600020548583815181106113d957fe5b60200260200101818152505060126000826001600160a01b03166001600160a01b031681526020019081526020016000205484838151811061141757fe5b602090810291909101015250600101611343565b5050909192565b61143a6116ce565b600054600160a81b900460ff1615611499576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b600054600160b01b900460ff16156115195733301461150757fe5b6000805460ff60b01b19169055611521565b6115216116ce565b565b61152b6116ce565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561157357600080fd5b505afa158015611587573d6000803e3d6000fd5b505050506040513d602081101561159d57600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b0319861681526001602081815260409092208451815584830151805191945061162193928501920190612d0f565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b3d604051818101604052816000823e82156116bf578181f35b8181fd5b600954620143700190565b6116d661045a565b6001600160a01b0316336001600160a01b031614611521576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b8051604080518082019091526008815267746f6f206d616e7960c01b6020820152600a8211156117705760405162461bcd60e51b8152600401610b569190613362565b50611779612959565b60005b8181101561097e5760006001600160a01b031683828151811061179b57fe5b6020026020010151600001516001600160a01b031614156040518060400160405280600c81526020016b61646472657373207a65726f60a01b815250906117f55760405162461bcd60e51b8152600401610b569190613362565b5060105460005b81811015611899576010818154811061181157fe5b60009182526020909120015485516001600160a01b039091169086908590811061183757fe5b6020026020010151600001516001600160a01b031614156040518060400160405280600b81526020016a647570206164647265737360a81b815250906118905760405162461bcd60e51b8152600401610b569190613362565b506001016117fc565b5060108483815181106118a857fe5b6020908102919091018101515182546001810184556000938452919092200180546001600160a01b0319166001600160a01b0390921691909117905583518490839081106118f257fe5b6020026020010151602001516011600086858151811061190e57fe5b6020026020010151600001516001600160a01b03166001600160a01b031681526020019081526020016000208190555060006012600086858151811061195057fe5b6020026020010151600001516001600160a01b03166001600160a01b03168152602001908152602001600020819055507f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb598483815181106119ad57fe5b60200260200101516000015160016040516119c992919061332d565b60405180910390a15060010161177c565b600083836040516020016119ef929190613191565b60408051808303601f190181529181528151602092830120600081815260139093529120805491925090600160c01b90046001600160401b0316611ad65760148054600180820183556000929092527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec01839055810180546001600160a01b0319166001600160a01b038716179055611a89846040612a09565b8051611a9f916002840191602090910190612d0f565b5060145460018201805467ffffffffffffffff60a01b1916600160a01b6000199093016001600160401b0316929092029190911790555b8054436001600160c01b038181166001600160401b03600160c01b80860482166001019091160291909316176001600160c01b0319169190911782556040517f7a459ed083a9b267865360013a5ad6dbc07e5befe6e4f71671c940fdd4206bee91611b4791889190889088906131d6565b60405180910390a1600b80546001600160c01b0319811660016001600160c01b0392831681018316919091178084559301549216600160a01b9092046001600160401b0316600160c01b0291909117905550505050565b6000600354431415611bb2575060006104ed565b43600355600d544790811115611f88576000611bdb600e54600d546128f690919063ffffffff16565b905080821415611d5057600e54600554611bf590826128f6565b6005556040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c90611c28908390613397565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611c8057600080fd5b505af193505050508015611c92575060015b611d0157611c9e61342b565b80611ca95750611cbb565b611cb5308260006119da565b50611cfc565b611cfc306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006119da565b611d4a565b600654611d0e90826128f6565b6006556040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a90611d41908390613397565b60405180910390a15b50611f86565b80821015611db2576000611d6f600d5484612abd90919063ffffffff16565b600754909150611d7f90826128f6565b6007556040517f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab36890611d41908390613397565b600e54600554611dc1916128f6565b600555600e54600d54600091611de291611ddc908690612abd565b90612abd565b600754909150611df290826128f6565b600755600e546040517fa42d823c276ad1990284418c303209194a75fa95a901f19752a9f65a407ffa8c91611e2691613397565b60405180910390a17f3fe36bcb00188390b2b40f1ab66c58f660aea67fe98b9f80667f692e1a9ab36881604051611e5d9190613397565b60405180910390a1600260009054906101000a90046001600160a01b03166001600160a01b031663c611c2c5600e546040518263ffffffff1660e01b81526004016000604051808303818588803b158015611eb757600080fd5b505af193505050508015611ec9575060015b611f3857611ed561342b565b80611ee05750611ef2565b611eec308260006119da565b50611f33565b611f33306040518060400160405280601d81526020017f756e6b6e6f776e206572726f722e20726563656976654d696e74696e6700000081525060006119da565b611f84565b600e54600654611f47916128f6565b600655600e546040517f12773bf711e11ec0b058c3856d441d726d2dc89113706c4f4175571f1e830c5a91611f7b91613397565b60405180910390a15b505b505b60105460005b818110156123a757600060108281548110611fa557fe5b60009182526020808320909101546001600160a01b031680835260129091526040909120549091508015612031576001600160a01b0382166000908152601260205260409081902060001983019055517f9895eddb1e8569b1dae526135aa5cab97f982fdc3b0ff7e17920c95e3b9bda629061202490849084906131bd565b60405180910390a161239d565b6001600160a01b038216600090815260116020526040812054905a90506204a76881101561209b577f9b5c4be38598cb8d8b6e07727d2303d1d9fc2dfc31ad323170f5ea4dcc1f914a85870360405161208a9190613397565b60405180910390a1505050506123a7565b620493df19810182158015906120b057508083105b156120b85750815b846001600160a01b0316636d0e8c34826040518263ffffffff1660e01b8152600401602060405180830381600088803b1580156120f457600080fd5b5087f193505050508015612125575060408051601f3d908101601f1916820190925261212291810190612fde565b60015b61235c5761213161342b565b8061213c57506121c3565b61214986825a86036119da565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561218457600080fd5b505af1158015612198573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121bc9190612fde565b5050612357565b60005a90506000841180156121e15750836121de8483612abd565b10155b156122b457612215866040518060400160405280600a8152602001696f7574206f662067617360b01b8152508386036119da565b6000866001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561225257600080fd5b505af1158015612266573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228a9190612fde565b9050806122ae57600c546001600160a01b0388166000908152601260205260409020555b50612355565b6122e086604051806040016040528060078152602001663ab735b737bbb760c91b8152508386036119da565b856001600160a01b031663e22fdece6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561231b57600080fd5b505af115801561232f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123539190612fde565b505b505b612399565b507fe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4855a84036040516123909291906131bd565b60405180910390a15b5050505b5050600101611f8e565b506123b0612b1a565b925082156123fa57600e8390556040517f34f843cef0df42035141347873da1758a6643358831b5ba5b1580be947644f92906123ed908590613397565b60405180910390a1612400565b6000600e555b47600d55600061240e612b33565b905047811461244957612449306040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b81525060006119da565b50505090565b6000808211612498576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b836124a557506000612556565b838302838582816124b257fe5b0414156124cb578281816124c257fe5b04915050612556565b60008386816124d657fe5b04905060008487816124e457fe5b06905060008587816124f257fe5b049050600086888161250057fe5b06905061254e61251a886125148685612b52565b90612bab565b6125486125278686612b52565b6125486125348987612b52565b6125488d6125428c8b612b52565b90612b52565b906128f6565b955050505050505b9392505050565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b838110156125c55781810151838201526020016125ad565b50505050905090810190601f1680156125f25780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b865181101561265a5786818151811061262857fe5b60200260200101518314156126525785818151811061264357fe5b6020026020010151915061265a565b600101612613565b506001600160a01b0381166126a5576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b60006126dc83836040518060400160405280600981526020016824b7333630ba34b7b760b91b815250612581565b6002546040519192507f4bdd1012a7d55ed9afad8675a125e1b68c7c15f712c0f3d5cddac69c3b9798059161271e9184916001600160a01b0390911690613348565b60405180910390a1600280546001600160a01b0319166001600160a01b038316179055600854612758576a31a17e847807b1bc0000006008555b60105480612767575050610d4c565b6000816001600160401b038111801561277f57600080fd5b506040519080825280602002602001820160405280156127b957816020015b6127a6612d9b565b81526020019060019003908161279e5790505b50905060005b828110156128c5576000601082815481106127d657fe5b600091825260208220015460408051637afadd3960e11b815290516001600160a01b039092169350612869928a928a92869263f5f5ba7292600480840193829003018186803b15801561282857600080fd5b505afa15801561283c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128649190810190613026565b612581565b83838151811061287557fe5b6020908102919091018101516001600160a01b03928316905290821660009081526011909152604090205483518490849081106128ae57fe5b6020908102919091018101510152506001016127bf565b506128cf8161172d565b5050505050565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76104ea81612c12565b600082820183811015612950576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60105460005b81811015610d4c576010805460009190600019810190811061297d57fe5b600091825260209091200154601080546001600160a01b03909216925090806129a257fe5b600082815260208120820160001990810180546001600160a01b03191690559091019091556040517f86d03f430c7616021073d7a71766f632f1ce19f289aa989534d9f4732253eb59916129f89184919061332d565b60405180910390a15060010161295f565b6060600083905082815111612a215783915050612953565b6000836001600160401b0381118015612a3957600080fd5b506040519080825280601f01601f191660200182016040528015612a64576020820181803683370190505b50905060005b84811015612ab457828181518110612a7e57fe5b602001015160f81c60f81b828281518110612a9557fe5b60200101906001600160f81b031916908160001a905350600101612a6a565b50949350505050565b600082821115612b14576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006104ea600554600454612abd90919063ffffffff16565b60006104ea600754612548600654600554612abd90919063ffffffff16565b600082612b6157506000612953565b82820282848281612b6e57fe5b04146129505760405162461bcd60e51b81526004018080602001828103825260218152602001806134e56021913960400191505060405180910390fd5b6000808211612c01576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381612c0a57fe5b049392505050565b600054600160a01b900460ff1615612c68576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b50805460018160011615610100020316600290046000825580601f10612cf15750610432565b601f0160209004906000526020600020908101906104329190612db2565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282612d455760008555612d8b565b82601f10612d5e57805160ff1916838001178555612d8b565b82800160010185558215612d8b579182015b82811115612d8b578251825591602001919060010190612d70565b50612d97929150612db2565b5090565b604080518082019091526000808252602082015290565b5b80821115612d975760008155600101612db3565b600082601f830112612dd7578081fd5b81356020612dec612de7836133dc565b6133b9565b8281528181019085830183850287018401881015612e08578586fd5b855b85811015612e2f578135612e1d816134cf565b84529284019290840190600101612e0a565b5090979650505050505050565b600060208284031215612e4d578081fd5b8135612950816134cf565b60008060408385031215612e6a578081fd5b82356001600160401b0380821115612e80578283fd5b818501915085601f830112612e93578283fd5b81356020612ea3612de7836133dc565b82815281810190858301838502870184018b1015612ebf578788fd5b8796505b84871015612ee1578035835260019690960195918301918301612ec3565b5096505086013592505080821115612ef7578283fd5b50612f0485828601612dc7565b9150509250929050565b60006020808385031215612f20578182fd5b82356001600160401b0380821115612f36578384fd5b818501915085601f830112612f49578384fd5b8135612f57612de7826133dc565b818152848101908486016040808502870188018b1015612f75578889fd5b8896505b84871015612fcf5780828c031215612f8f578889fd5b80518181018181108882111715612fa257fe5b82528235612faf816134cf565b815282890135898201528452600196909601959287019290810190612f79565b50909998505050505050505050565b600060208284031215612fef578081fd5b81518015158114612950578182fd5b60006020828403121561300f578081fd5b81356001600160e01b031981168114612950578182fd5b600060208284031215613037578081fd5b81516001600160401b038082111561304d578283fd5b818401915084601f830112613060578283fd5b81518181111561306c57fe5b61307f601f8201601f19166020016133b9565b9150808252856020828501011115613095578384fd5b612ab48160208401602086016133f9565b6000602082840312156130b7578081fd5b5035919050565b600080604083850312156130d0578182fd5b50508035926020909101359150565b6000815180845260208085019450808401835b838110156131175781516001600160a01b0316875295820195908201906001016130f2565b509495945050505050565b6000815180845260208085019450808401835b8381101561311757815187529582019590820190600101613135565b600081518084526131698160208601602086016133f9565b601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03831681526040602082018190526000906131b590830184613151565b949350505050565b6001600160a01b03929092168252602082015260400190565b600060018060a01b0386168252846020830152608060408301526131fd6080830185613151565b905082606083015295945050505050565b606080825284519082018190526000906020906080840190828801845b828110156132505781516001600160a01b03168452928401929084019060010161322b565b505050838103828501526132648187613122565b91505082810360408401526132798185613122565b9695505050505050565b600060a0825261329660a0830188613122565b6020838203818501526132a98289613122565b848103604086015287518082529092508183019082810284018301838a01865b838110156132f757601f198784030185526132e5838351613151565b948601949250908501906001016132c9565b5050868103606088015261330b818a6130df565b955050505050508260808301529695505050505050565b901515815260200190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6000602082526125566020830184613151565b6001600160c01b039290921682526001600160401b0316602082015260400190565b90815260200190565b6000838252604060208301526131b56040830184613151565b6040518181016001600160401b03811182821017156133d457fe5b604052919050565b60006001600160401b038211156133ef57fe5b5060209081020190565b60005b838110156134145781810151838201526020016133fc565b8381111561083f5750506000910152565b60e01c90565b600060443d101561343b576104ed565b600481823e6308c379a061344f8251613425565b14613459576104ed565b6040513d600319016004823e80513d6001600160401b03816024840111818411171561348857505050506104ed565b828401925082519150808211156134a257505050506104ed565b503d830160208284010111156134ba575050506104ed565b601f01601f1916810160200160405291505090565b6001600160a01b038116811461043257600080fdfe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122024c81cc382b52201889f886cbc353194ac8eb254f5c2d35cb70533439e54edc564736f6c63430007060033\"},\"0x1000000000000000000000000000000000000003\":{\"balance\":\"0x0\",\"code\":\"0x608060405234801561001057600080fd5b506004361061018e5760003560e01c80639d986f91116100de578063c9f960eb11610097578063e17f212e11610071578063e17f212e1461064c578063e2db5a5214610668578063f5a9838314610794578063ffacb84e1461079c5761018e565b8063c9f960eb1461061f578063cd4b691414610627578063d89601fd146106445761018e565b80639d986f91146103f95780639ec2b58114610425578063aea36b53146104c6578063b00c0b76146104ec578063b39c68581461060f578063c0156bcc146106175761018e565b806371e1fad91161014b5780637ac420ad116101255780637ac420ad146103825780638c9d28b6146103a85780638fc6f667146103b05780639d6a890f146103d35761018e565b806371e1fad91461023157806374e6310e1461023957806376794efb146102df5761018e565b80633b56f098146101935780635267a15d146101ad5780635aa6e675146101d15780635ff27079146101d957806362354e031461020257806367fc40291461020a575b600080fd5b61019b6107f4565b60408051918252519081900360200190f35b6101b56107fc565b604080516001600160a01b039092168252519081900360200190f35b6101b5610821565b610200600480360360208110156101ef57600080fd5b50356001600160e01b0319166108b7565b005b6101b5610c0c565b6102006004803603602081101561022057600080fd5b50356001600160e01b031916610c17565b6101b5610cff565b6102606004803603602081101561024f57600080fd5b50356001600160e01b031916610d0e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156102a357818101518382015260200161028b565b50505050905090810190601f1680156102d05780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b610200600480360360408110156102f557600080fd5b810190602081018135600160201b81111561030f57600080fd5b82018360208201111561032157600080fd5b803590602001918460208302840111600160201b8311171561034257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610db4915050565b61019b6004803603602081101561039857600080fd5b50356001600160a01b0316610ecd565b6101b5610ee8565b610200600480360360408110156103c657600080fd5b5080359060200135610ef7565b610200600480360360208110156103e957600080fd5b50356001600160a01b0316611180565b6102006004803603604081101561040f57600080fd5b506001600160a01b038135169060200135611185565b6102006004803603602081101561043b57600080fd5b810190602081018135600160201b81111561045557600080fd5b82018360208201111561046757600080fd5b803590602001918460208302840111600160201b8311171561048857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611235945050505050565b610200600480360360208110156104dc57600080fd5b50356001600160a01b0316611384565b6102006004803603604081101561050257600080fd5b810190602081018135600160201b81111561051c57600080fd5b82018360208201111561052e57600080fd5b803590602001918460208302840111600160201b8311171561054f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561059e57600080fd5b8201836020820111156105b057600080fd5b803590602001918460208302840111600160201b831117156105d157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611451945050505050565b6101b56114fc565b61019b61150b565b6101b5611510565b61019b6004803603602081101561063d57600080fd5b5035611530565b61019b61154a565b6106546115f4565b604080519115158252519081900360200190f35b6102006004803603608081101561067e57600080fd5b81359190810190604081016020820135600160201b81111561069f57600080fd5b8201836020820111156106b157600080fd5b803590602001918460208302840111600160201b831117156106d257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561072157600080fd5b82018360208201111561073357600080fd5b803590602001918460208302840111600160201b8311171561075457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250611604915050565b610200611e1f565b6107a4611ed9565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156107e05781810151838201526020016107c8565b505050509050019250505060405180910390f35b600160801b81565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b60008054600160a81b900460ff16610844576000546001600160a01b03166108b1565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b15801561088457600080fd5b505afa158015610898573d6000803e3d6000fd5b505050506040513d60208110156108ae57600080fd5b50515b90505b90565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b1580156108fb57600080fd5b505afa15801561090f573d6000803e3d6000fd5b505050506040513d602081101561092557600080fd5b5051610968576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b03198116600090815260016020526040902080546109d4576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b8054421015610a2a576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ac45780601f10610a9957610100808354040283529160200191610ac4565b820191906000526020600020905b815481529060010190602001808311610aa757829003601f168201915b5050506001600160e01b0319861660009081526001602081905260408220828155949550909250610af8915083018261247b565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b60208310610b405780518252601f199092019160209182019101610b21565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610ba2576040519150601f19603f3d011682016040523d82523d6000602084013e610ba7565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a1610c0681611f3b565b50505050565b60076001609c1b0181565b610c1f611f58565b6001600160e01b03198116600090815260016020526040902054610c8a576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b03198116600090815260016020819052604082208281559190610cfa9083018261247b565b505050565b6004546001600160a01b031690565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f81018590048502860185019096528585529094919392909190830182828015610daa5780601f10610d7f57610100808354040283529160200191610daa565b820191906000526020600020905b815481529060010190602001808311610d8d57829003601f168201915b5050505050905082565b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b03163314610e785760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610e3d578181015183820152602001610e25565b50505050905090810190601f168015610e6a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060005b8251811015610cfa57816001901b1960056000858481518110610e9b57fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002080549091169055600101610e7c565b6001600160a01b031660009081526005602052604090205490565b6002546001600160a01b031690565b600360009054906101000a90046001600160a01b03166001600160a01b03166308a7f4026040518163ffffffff1660e01b815260040160206040518083038186803b158015610f4557600080fd5b505afa158015610f59573d6000803e3d6000fd5b505050506040513d6020811015610f6f57600080fd5b505160408051808201909152600e81526d15dc9bdb99c8195c1bd8da081a5960921b6020820152908314610fe45760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b506000828152600860209081526040808320338452825291829020548251808401909352601983527f4475706c6963617465207375626d697420696e2065706f63680000000000000091830191909152156110805760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b50336000908152600560205260409020541515806110ad57503360009081526007602052604090205460ff165b6040518060400160405280600f81526020016e139bdd081dda1a5d195b1a5cdd1959608a1b815250906111215760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b50600082815260086020908152604080832033808552908352928190208490558051848152429281019290925280518593927f5e2f64e70eafef31c2f48c8ef140b36406531c36ab0faaede30843202c16f6a892908290030190a35050565bfe5b50565b600454604080518082019091526016815275566f7465722077686974656c6973746572206f6e6c7960501b6020820152906001600160a01b0316331461120c5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b506001600160a01b0390911660009081526005602052604090208054600190921b919091179055565b6003546040805180820190915260118152704654534f206d616e61676572206f6e6c7960781b6020820152906001600160a01b031633146112b75760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b5060065460005b8181101561131557600060076000600684815481106112d957fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff19169115159190911790556001016112be565b5050805160005b818110156113705760016007600085848151811061133657fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905560010161131c565b508151610cfa9060069060208501906124bf565b600054600160b01b900460ff16806113a65750600054600160a81b900460ff16155b15611446576113b3611fb9565b60006113bd6107fc565b6001600160a01b0316146040518060400160405280600b81526020016a105b1c9958591e481cd95d60aa1b815250906114375760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b5061144181611fee565b611182565b611182600036612012565b6114596107fc565b6001600160a01b0316336001600160a01b0316146114b5576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b6114ee6114e983836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b815250612195565b611fee565b6114f882826122c2565b5050565b6003546001600160a01b031690565b603281565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76108b1816123c2565b60006009603283066032811061154257fe5b015492915050565b600080600360009054906101000a90046001600160a01b03166001600160a01b03166308a7f4026040518163ffffffff1660e01b815260040160206040518083038186803b15801561159b57600080fd5b505afa1580156115af573d6000803e3d6000fd5b505050506040513d60208110156115c557600080fd5b50519050806115d85760009150506108b4565b60096032600019830106603281106115ec57fe5b015491505090565b600054600160a81b900460ff1681565b8251825160408051808201909152601a81527f4172726179206c656e6774687320646f206e6f74206d61746368000000000000602082015290821461168a5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b5060408051808201909152601781527f546f6f20736d616c6c2072616e646f6d206e756d6265720000000000000000006020820152600160801b8310156117125760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b5083838333604051602001808060200180602001858152602001846001600160a01b03168152602001838103835287818151815260200191508051906020019060200280838360005b8381101561177357818101518382015260200161175b565b50505050905001838103825286818151815260200191508051906020019060200280838360005b838110156117b257818101518382015260200161179a565b50505050905001965050505050505060405160208183030381529060405280519060200120600860008781526020019081526020016000206000336001600160a01b03166001600160a01b0316815260200190815260200160002054146040518060600160405280602381526020016125b660239139906118745760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3d578181015183820152602001610e25565b506002546040516313968ea760e31b81526020600482018181528751602484015287516000946001600160a01b031693639cb47538938a939283926044019180860191028083838b5b838110156118d55781810151838201526020016118bd565b505050509050019250505060006040518083038186803b1580156118f857600080fd5b505afa15801561190c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561193557600080fd5b8101908080516040519392919084600160201b82111561195457600080fd5b90830190602082018581111561196957600080fd5b82518660208202830111600160201b8211171561198557600080fd5b82525081516020918201928201910280838360005b838110156119b257818101518382015260200161199a565b5050505091909101604090815233600090815260056020529081205495965093508392505050846119e4576000611a7c565b836000815181106119f157fe5b60200260200101516001600160a01b031663f72cab28338b6040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015611a4f57600080fd5b505af1158015611a63573d6000803e3d6000fd5b505050506040513d6020811015611a7957600080fd5b50515b90506000805b86811015611c8f578015801590611aac5750898181518110611aa057fe5b60200260200101518210155b15611b2957604080518082018252601b81527f4654534f20696e6469636573206e6f7420696e6372656173696e6700000000006020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610e3d578181015183820152602001610e25565b898181518110611b3557fe5b60200260200101519150816001901b851660001415611bde5783611bde573360009081526007602052604090205460ff1615611b745760019350611bde565b604080518082018252600f81526e139bdd081dda1a5d195b1a5cdd1959608a1b6020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610e3d578181015183820152602001610e25565b858181518110611bea57fe5b60200260200101516001600160a01b031663c1f6c36e338d8c8581518110611c0e57fe5b6020026020010151876040518563ffffffff1660e01b815260040180856001600160a01b03168152602001848152602001838152602001828152602001945050505050600060405180830381600087803b158015611c6b57600080fd5b505af1158015611c7f573d6000803e3d6000fd5b505060019092019150611a829050565b5060008a8152600860209081526040808320338452825280832083905580518083018b81528183019283528c5160608301528c518c958e95929493608001928681019202908190849084905b83811015611cf3578181015183820152602001611cdb565b5050505090500193505050506040516020818303038152906040528051906020012060001c600960328c81611d2457fe5b0660328110611d2f57fe5b016000828254019250508190555089336001600160a01b03167fafffa539ac1cad89751c875d871abadc6deb7bd51bf6baea004fc71ca0a48fa5878b8b42604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b83811015611dbd578181015183820152602001611da5565b50505050905001838103825286818151815260200191508051906020019060200280838360005b83811015611dfc578181015183820152602001611de4565b50505050905001965050505050505060405180910390a350505050505050505050565b611e27611f58565b600054600160a81b900460ff1615611e86576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b60606006805480602002602001604051908101604052809291908181526020018280548015611f3157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611f13575b5050505050905090565b3d604051818101604052816000823e8215611f54578181f35b8181fd5b611f60610821565b6001600160a01b0316336001600160a01b031614611fb7576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b565b600054600160b01b900460ff1615611fe657333014611fd457fe5b6000805460ff60b01b19169055611fb7565b611fb7611f58565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b61201a611f58565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561206257600080fd5b505afa158015612076573d6000803e3d6000fd5b505050506040513d602081101561208c57600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b0319861681526001602081815260409092208451815584830151805191945061211093928501920190612524565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b838110156121d95781810151838201526020016121c1565b50505050905090810190601f1680156122065780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b865181101561226e5786818151811061223c57fe5b60200260200101518314156122665785818151811061225757fe5b6020026020010151915061226e565b600101612227565b506001600160a01b0381166122b9576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b6122f182826040518060400160405280600c81526020016b4674736f526567697374727960a01b815250612195565b600260006101000a8154816001600160a01b0302191690836001600160a01b0316021790555061234582826040518060400160405280600b81526020016a233a39b7a6b0b730b3b2b960a91b815250612195565b600360006101000a8154816001600160a01b0302191690836001600160a01b0316021790555061239e82826040518060400160405280601081526020016f2b37ba32b92bb434ba32b634b9ba32b960811b815250612195565b600480546001600160a01b0319166001600160a01b03929092169190911790555050565b600054600160a01b900460ff1615612418576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b50805460018160011615610100020316600290046000825580601f106124a15750611182565b601f01602090049060005260206000209081019061118291906125a0565b828054828255906000526020600020908101928215612514579160200282015b8281111561251457825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906124df565b506125209291506125a0565b5090565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261255a5760008555612514565b82601f1061257357805160ff1916838001178555612514565b82800160010185558215612514579182015b82811115612514578251825591602001919060010190612585565b5b8082111561252057600081556001016125a156fe507269636520616c72656164792072657665616c6564206f72206e6f742076616c6964a2646970667358221220f6777819e200850a356b0598642d029e083034aaf79d7e264a6cdf6b21e786b364736f6c63430007060033\"},\"0x1000000000000000000000000000000000000004\":{\"balance\":\"0x476a3d4ef278dc3746d6bac0\",\"code\":\"0x608060405234801561001057600080fd5b506004361061010b5760003560e01c806375a0fef9116100a2578063d8952a4911610071578063d8952a491461029a578063e17f212e146102c8578063e1a1a5dc146102e4578063ec8d87771461030a578063f5a98383146103275761010b565b806375a0fef91461025c57806395645e34146102645780639d6a890f1461026c578063c9f960eb146102925761010b565b8063616d7c9f116100de578063616d7c9f1461017f57806362354e031461018757806367fc40291461018f57806374e6310e146101b65761010b565b8063100223bb1461011057806329d71f6d1461012a5780635aa6e675146101325780635ff2707914610156575b600080fd5b61011861032f565b60408051918252519081900360200190f35b610118610335565b61013a610345565b604080516001600160a01b039092168252519081900360200190f35b61017d6004803603602081101561016c57600080fd5b50356001600160e01b0319166103da565b005b61013a61072f565b61013a61073e565b61017d600480360360208110156101a557600080fd5b50356001600160e01b031916610749565b6101dd600480360360208110156101cc57600080fd5b50356001600160e01b031916610831565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610220578181015183820152602001610208565b50505050905090810190601f16801561024d5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b61013a6108d7565b61013a6108e6565b61017d6004803603602081101561028257600080fd5b50356001600160a01b03166108f5565b61013a6108fa565b61017d600480360360408110156102b057600080fd5b506001600160a01b038135811691602001351661091a565b6102d0610aef565b604080519115158252519081900360200190f35b61017d600480360360208110156102fa57600080fd5b50356001600160a01b0316610aff565b61017d6004803603602081101561032057600080fd5b5035610c8b565b61017d610e29565b60055481565b6b0257b4b8c0aa5cf8f500000081565b60008054600160a81b900460ff16610368576000546001600160a01b03166103d5565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b1580156103a857600080fd5b505afa1580156103bc573d6000803e3d6000fd5b505050506040513d60208110156103d257600080fd5b50515b905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b15801561041e57600080fd5b505afa158015610432573d6000803e3d6000fd5b505050506040513d602081101561044857600080fd5b505161048b576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b03198116600090815260016020526040902080546104f7576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b805442101561054d576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105e75780601f106105bc576101008083540402835291602001916105e7565b820191906000526020600020905b8154815290600101906020018083116105ca57829003601f168201915b5050506001600160e01b031986166000908152600160208190526040822082815594955090925061061b915083018261129d565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106106635780518252601f199092019160209182019101610644565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146106c5576040519150601f19603f3d011682016040523d82523d6000602084013e6106ca565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a161072981610ee3565b50505050565b6004546001600160a01b031681565b60076001609c1b0181565b610751610f00565b6001600160e01b031981166000908152600160205260409020546107bc576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b0319811660009081526001602081905260408220828155919061082c9083018261129d565b505050565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f810185900485028601850190965285855290949193929091908301828280156108cd5780601f106108a2576101008083540402835291602001916108cd565b820191906000526020600020905b8154815290600101906020018083116108b057829003601f168201915b5050505050905082565b6002546001600160a01b031681565b6003546001600160a01b031681565bfe5b50565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76103d581610f61565b600054600160b01b900460ff168061093c5750600054600160a81b900460ff16155b15610ae05761094961101a565b6003546001600160a01b031615801561096b57506004546001600160a01b0316155b6040518060400160405280600b81526020016a185b1c9958591e481cd95d60aa1b81525090610a185760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156109dd5781810151838201526020016109c5565b50505050905090810190601f168015610a0a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506001600160a01b03821615801590610a3957506001600160a01b03811615155b6040518060400160405280600c81526020016b61646472657373207a65726f60a01b81525090610aaa5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109dd5781810151838201526020016109c5565b50600380546001600160a01b038085166001600160a01b0319928316179092556004805492841692909116919091179055610aeb565b610aeb60003661104f565b5050565b600054600160a81b900460ff1681565b600054600160b01b900460ff1680610b215750600054600160a81b900460ff16155b15610c8057610b2e61101a565b60025460408051808201909152600b81526a185b1c9958591e481cd95d60aa1b6020820152906001600160a01b031615610ba95760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109dd5781810151838201526020016109c5565b506003546001600160a01b0382811691161480610bd357506004546001600160a01b038281169116145b6040518060400160405280600d81526020016c77726f6e67206164647265737360981b81525090610c455760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109dd5781810151838201526020016109c5565b50600280546001600160a01b0319166001600160a01b038381169182179092556003549091161415610c7b57610c7b81476111d2565b6108f7565b6108f760003661104f565b6002546001600160a01b031633148015610caf57506004546001600160a01b031633145b60405180604001604052806011815260200170646973747269627574696f6e206f6e6c7960781b81525090610d255760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109dd5781810151838201526020016109c5565b504262263b80600554011115604051806040016040528060098152602001683a37b79037b33a32b760b91b81525090610d9f5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109dd5781810151838201526020016109c5565b506040805180820190915260088152670e8dede40daeac6d60c31b60208201526b0257b4b8c0aa5cf8f5000000821115610e1a5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109dd5781810151838201526020016109c5565b50426005556108f733826111d2565b610e31610f00565b600054600160a81b900460ff1615610e90576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b3d604051818101604052816000823e8215610efc578181f35b8181fd5b610f08610345565b6001600160a01b0316336001600160a01b031614610f5f576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b565b600054600160a01b900460ff1615610fb7576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b600054600160b01b900460ff16156110475733301461103557fe5b6000805460ff60b01b19169055610f5f565b610f5f610f00565b611057610f00565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561109f57600080fd5b505afa1580156110b3573d6000803e3d6000fd5b505050506040513d60208110156110c957600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b0319861681526001602081815260409092208451815584830151805191945061114d939285019201906112e1565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b6040516000906001600160a01b0384169083908381818185875af1925050503d806000811461121d576040519150601f19603f3d011682016040523d82523d6000602084013e611222565b606091505b5050905080604051806040016040528060118152602001701cd95b9908199d5b991cc819985a5b1959607a1b815250906107295760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109dd5781810151838201526020016109c5565b50805460018160011615610100020316600290046000825580601f106112c357506108f7565b601f0160209004906000526020600020908101906108f7919061136d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282611317576000855561135d565b82601f1061133057805160ff191683800117855561135d565b8280016001018555821561135d579182015b8281111561135d578251825591602001919060010190611342565b5061136992915061136d565b5090565b5b80821115611369576000815560010161136e56fea264697066735822122023f7cb20d6e161023a579061b9301b398dd68992257d7ca8390e83d839609dd764736f6c63430007060033\"},\"0x1000000000000000000000000000000000000005\":{\"balance\":\"0x409f9cbc7c4a04c220000000\",\"code\":\"0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c806397249db71161008c578063d4456c9711610066578063d4456c9714610269578063e17f212e1461028f578063ec8d8777146102ab578063f5a98383146102c8576100ea565b806397249db7146102335780639d6a890f1461023b578063c9f960eb14610261576100ea565b80635ff27079116100c85780635ff270791461013557806362354e031461015e57806367fc40291461016657806374e6310e1461018d576100ea565b8063100223bb146100ef5780634841422e146101095780635aa6e6751461012d575b600080fd5b6100f76102d0565b60408051918252519081900360200190f35b6101116102d6565b604080516001600160a01b039092168252519081900360200190f35b6101116102e5565b61015c6004803603602081101561014b57600080fd5b50356001600160e01b03191661037a565b005b6101116106cf565b61015c6004803603602081101561017c57600080fd5b50356001600160e01b0319166106da565b6101b4600480360360208110156101a357600080fd5b50356001600160e01b0319166107c2565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156101f75781810151838201526020016101df565b50505050905090810190601f1680156102245780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b6100f7610868565b61015c6004803603602081101561025157600080fd5b50356001600160a01b0316610877565b61011161087c565b61015c6004803603602081101561027f57600080fd5b50356001600160a01b031661089c565b6102976109af565b604080519115158252519081900360200190f35b61015c600480360360208110156102c157600080fd5b50356109bf565b61015c610bf8565b60035481565b6002546001600160a01b031681565b60008054600160a81b900460ff16610308576000546001600160a01b0316610375565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b15801561034857600080fd5b505afa15801561035c573d6000803e3d6000fd5b505050506040513d602081101561037257600080fd5b50515b905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b1580156103be57600080fd5b505afa1580156103d2573d6000803e3d6000fd5b505050506040513d60208110156103e857600080fd5b505161042b576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610497576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b80544210156104ed576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105875780601f1061055c57610100808354040283529160200191610587565b820191906000526020600020905b81548152906001019060200180831161056a57829003601f168201915b5050506001600160e01b03198616600090815260016020819052604082208281559495509092506105bb9150830182610fa1565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106106035780518252601f1990920191602091820191016105e4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610665576040519150601f19603f3d011682016040523d82523d6000602084013e61066a565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a16106c981610cb2565b50505050565b60076001609c1b0181565b6106e2610ccf565b6001600160e01b0319811660009081526001602052604090205461074d576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b031981166000908152600160208190526040822082815591906107bd90830182610fa1565b505050565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f8101859004850286018501909652858552909491939290919083018282801561085e5780601f106108335761010080835404028352916020019161085e565b820191906000526020600020905b81548152906001019060200180831161084157829003601f168201915b5050505050905082565b6a14adf4b7320334b900000081565bfe5b50565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd761037581610d30565b600054600160b01b900460ff16806108be5750600054600160a81b900460ff16155b156109a4576108cb610de9565b60025460408051808201909152600b81526a185b1c9958591e481cd95d60aa1b6020820152906001600160a01b0316156109835760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610948578181015183820152602001610930565b50505050905090810190601f1680156109755780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600280546001600160a01b0319166001600160a01b038316179055610879565b610879600036610e1e565b600054600160a81b900460ff1681565b600254604080518082019091526013815272696e63656e7469766520706f6f6c206f6e6c7960681b6020820152906001600160a01b03163314610a435760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610948578181015183820152602001610930565b504262014370600354011115604051806040016040528060098152602001683a37b79037b33a32b760b91b81525090610abd5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610948578181015183820152602001610930565b506040805180820190915260088152670e8dede40daeac6d60c31b60208201526a14adf4b7320334b9000000821115610b375760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610948578181015183820152602001610930565b5042600355604051600090339083908381818185875af1925050503d8060008114610b7e576040519150601f19603f3d011682016040523d82523d6000602084013e610b83565b606091505b50509050806040518060400160405280600b81526020016a1c1d5b1b0819985a5b195960aa1b815250906107bd5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610948578181015183820152602001610930565b610c00610ccf565b600054600160a81b900460ff1615610c5f576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b3d604051818101604052816000823e8215610ccb578181f35b8181fd5b610cd76102e5565b6001600160a01b0316336001600160a01b031614610d2e576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b565b600054600160a01b900460ff1615610d86576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b600054600160b01b900460ff1615610e1657333014610e0457fe5b6000805460ff60b01b19169055610d2e565b610d2e610ccf565b610e26610ccf565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e6e57600080fd5b505afa158015610e82573d6000803e3d6000fd5b505050506040513d6020811015610e9857600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b03198616815260016020818152604090922084518155848301518051919450610f1c93928501920190610fe5565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b50805460018160011615610100020316600290046000825580601f10610fc75750610879565b601f0160209004906000526020600020908101906108799190611071565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261101b5760008555611061565b82601f1061103457805160ff1916838001178555611061565b82800160010185558215611061579182015b82811115611061578251825591602001919060010190611046565b5061106d929150611071565b5090565b5b8082111561106d576000815560010161107256fea26469706673582212202553285371ed9813b00e179bc0891ef5c20c5625a744d5d7bb3ea8f2a389163e64736f6c63430007060033\"},\"0x1000000000000000000000000000000000000006\":{\"balance\":\"0x0c9a470dee8dcc8239ad6c40\",\"code\":\"0x608060405234801561001057600080fd5b50600436106101375760003560e01c806374e6310e116100b8578063c9f960eb1161007c578063c9f960eb146103d7578063ce592dfb146103df578063d5261971146103e7578063e17f212e146103ef578063ea1e63211461040b578063f5a983831461043157610137565b806374e6310e146102de5780638dc7b1a91461038457806390568f81146103a15780639d6a890f146103a9578063c602a77e146103cf57610137565b80635ff27079116100ff5780635ff27079146101a757806361235585146101d057806362354e031461029257806367fc40291461029a5780636de80541146102c157610137565b80633f630c3a1461013c57806346a549b5146101565780634a4b698a1461015e5780635aa6e675146101975780635dcb01701461019f575b600080fd5b610144610439565b60408051918252519081900360200190f35b61014461043f565b61017b6004803603602081101561017457600080fd5b5035610445565b604080516001600160a01b039092168252519081900360200190f35b61017b61046f565b610144610504565b6101ce600480360360208110156101bd57600080fd5b50356001600160e01b03191661050a565b005b6101ce600480360360408110156101e657600080fd5b81019060208101813564010000000081111561020157600080fd5b82018360208201111561021357600080fd5b8035906020019184602083028401116401000000008311171561023557600080fd5b91939092909160208101903564010000000081111561025357600080fd5b82018360208201111561026557600080fd5b8035906020019184602083028401116401000000008311171561028757600080fd5b50909250905061085f565b61017b610bd5565b6101ce600480360360208110156102b057600080fd5b50356001600160e01b031916610be0565b6101ce600480360360208110156102d757600080fd5b5035610cc8565b610305600480360360208110156102f457600080fd5b50356001600160e01b031916610ed5565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610348578181015183820152602001610330565b50505050905090810190601f1680156103755780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b6101ce6004803603602081101561039a57600080fd5b5035610f7b565b610144611027565b6101ce600480360360208110156103bf57600080fd5b50356001600160a01b031661102d565b61014461102f565b61017b611035565b610144611055565b6101ce61105b565b6103f7611303565b604080519115158252519081900360200190f35b6101446004803603602081101561042157600080fd5b50356001600160a01b0316611313565b6101ce611325565b60055481565b60075481565b6003818154811061045557600080fd5b6000918252602090912001546001600160a01b0316905081565b60008054600160a81b900460ff16610492576000546001600160a01b03166104ff565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b1580156104d257600080fd5b505afa1580156104e6573d6000803e3d6000fd5b505050506040513d60208110156104fc57600080fd5b50515b905090565b60065481565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b15801561054e57600080fd5b505afa158015610562573d6000803e3d6000fd5b505050506040513d602081101561057857600080fd5b50516105bb576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610627576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b805442101561067d576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107175780601f106106ec57610100808354040283529160200191610717565b820191906000526020600020905b8154815290600101906020018083116106fa57829003601f168201915b5050506001600160e01b031986166000908152600160208190526040822082815594955090925061074b915083018261198b565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106107935780518252601f199092019160209182019101610774565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146107f5576040519150601f19603f3d011682016040523d82523d6000602084013e6107fa565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a1610859816113df565b50505050565b600054600160b01b900460ff16806108815750600054600160a81b900460ff16155b15610bca5761088e6113fc565b604080518082019091526008815267746f6f206d616e7960c01b60208201526103e884111561093b5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156109005781810151838201526020016108e8565b50505050905090810190601f16801561092d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060408051808201909152601781527f617272617973206c656e67746873206d69736d6174636800000000000000000060208201528382146109be5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b5060065460408051808201909152600f81526e185b1c9958591e481cdd185c9d1959608a1b60208201529015610a355760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b5060006004600086866000818110610a4957fe5b905060200201356001600160a01b03166001600160a01b03166001600160a01b03168152602001908152602001600020541115610a8557610bc5565b60005b61ffff8116841115610b6057600085858361ffff16818110610aa657fe5b905060200201356001600160a01b031690506000610ae96105dc61271087878761ffff16818110610ad357fe5b905060200201356114339092919063ffffffff16565b60038054600181019091557fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0180546001600160a01b0319166001600160a01b0385169081179091556000908152600460205260409020819055600554909150610b539082611541565b6005555050600101610a88565b507fc21490756c6f0185a8ad2363084fd0a45b06707979f77786b5e681bddc1d2fa1848460405180806020018281038252848482818152602001925060200280828437600083820152604051601f909101601f19169092018290039550909350505050a15b610859565b6108596000366115a4565b60076001609c1b0181565b610be8611727565b6001600160e01b03198116600090815260016020526040902054610c53576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b03198116600090815260016020819052604082208281559190610cc39083018261198b565b505050565b600054600160b01b900460ff1680610cea5750600054600160a81b900460ff16155b15610ec757610cf76113fc565b6006541580610d07575042600654115b6040518060400160405280600f81526020016e185b1c9958591e481cdd185c9d1959608a1b81525090610d7b5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b5080600654108015610d8f57506007548111155b60405180604001604052806015815260200174077726f6e672073746172742074696d657374616d7605c1b81525090610e095760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b5060068190556040805182815290517fd17095b0cb6319c5430ce8553f9ce78001789e1f94f47f377653a16198f2a90b9181900360200190a147610e4b611786565b11156040518060400160405280600f81526020016e62616c616e636520746f6f206c6f7760881b81525090610ec15760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b50610ed2565b610ed26000366115a4565b50565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f81018590048502860185019096528585529094919392909190830182828015610f715780601f10610f4657610100808354040283529160200191610f71565b820191906000526020600020905b815481529060010190602001808311610f5457829003601f168201915b5050505050905082565b600054600160b01b900460ff1680610f9d5750600054600160a81b900460ff16155b15610ec757610faa6113fc565b60075460408051808201909152600b81526a185b1c9958591e481cd95d60aa1b6020820152901561101c5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b506007819055610ed2565b60085481565bfe5b60095481565b600073fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd76104ff8161179f565b60035490565b6006541580159061106d575042600654105b6040518060400160405280600b81526020016a1b9bdd081cdd185c9d195960aa1b815250906110dd5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b50600280541415611135576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002805560095460035460009161115191603290910190611858565b6009549091505b8181101561127b5760006003828154811061116f57fe5b60009182526020808320909101546001600160a01b031680835260049091526040822080549290556003805491935090849081106111a957fe5b600091825260209091200180546001600160a01b03191690556008546111cf9082611541565b6008556040516000906001600160a01b0384169061520890849084818181858888f193505050503d8060008114611222576040519150601f19603f3d011682016040523d82523d6000602084013e611227565b606091505b5050905080611270576040805183815290516001600160a01b038516917fa63e265bae965e6aae4283d17bf7ba298580ba5924f711fe7da1a7ee4ae19ef5919081900360200190a25b505050600101611158565b5060095560016002554761128d611786565b11156040518060400160405280600f81526020016e62616c616e636520746f6f206c6f7760881b81525090610ed25760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156109005781810151838201526020016108e8565b600054600160a81b900460ff1681565b60046020526000908152604090205481565b61132d611727565b600054600160a81b900460ff161561138c576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b3d604051818101604052816000823e82156113f8578181f35b8181fd5b600054600160b01b900460ff16156114295733301461141757fe5b6000805460ff60b01b19169055611431565b611431611727565b565b600080821161147c576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b836114895750600061153a565b8383028385828161149657fe5b0414156114af578281816114a657fe5b0491505061153a565b60008386816114ba57fe5b04905060008487816114c857fe5b06905060008587816114d657fe5b04905060008688816114e457fe5b0690506115326114fe886114f8868561186e565b906118c7565b61152c61150b868661186e565b61152c611518898761186e565b61152c8d6115268c8b61186e565b9061186e565b90611541565b955050505050505b9392505050565b60008282018381101561159b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b6115ac611727565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115f457600080fd5b505afa158015611608573d6000803e3d6000fd5b505050506040513d602081101561161e57600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b031986168152600160208181526040909220845181558483015180519194506116a2939285019201906119cf565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b61172f61046f565b6001600160a01b0316336001600160a01b031614611431576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60006104ff60085460055461192e90919063ffffffff16565b600054600160a01b900460ff16156117f5576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b6000818310611867578161159b565b5090919050565b60008261187d5750600061159e565b8282028284828161188a57fe5b041461159b5760405162461bcd60e51b8152600401808060200182810382526021815260200180611a716021913960400191505060405180910390fd5b600080821161191d576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161192657fe5b049392505050565b600082821115611985576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b50805460018160011615610100020316600290046000825580601f106119b15750610ed2565b601f016020900490600052602060002090810190610ed29190611a5b565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282611a055760008555611a4b565b82601f10611a1e57805160ff1916838001178555611a4b565b82800160010185558215611a4b579182015b82811115611a4b578251825591602001919060010190611a30565b50611a57929150611a5b565b5090565b5b80821115611a575760008155600101611a5c56fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a2646970667358221220de084bcc806438ade1d29f53684bb8a7c861697d43c2ed767e4eee2004867fda64736f6c63430007060033\"},\"0x1000000000000000000000000000000000000007\":{\"balance\":\"0x0\",\"code\":\"0x608060405234801561001057600080fd5b50600436106100935760003560e01c80637ff6faa6116100665780637ff6faa614610198578063cf0ea268146101a0578063cfc1625414610258578063debfda301461027e578063ef09e78f146102b857610093565b80631d452e46146100985780631e891c0a1461013d5780636221a54b1461015a5780637325249414610174575b600080fd5b61013b600480360360208110156100ae57600080fd5b8101906020810181356401000000008111156100c957600080fd5b8201836020820111156100db57600080fd5b803590602001918460208302840111640100000000831117156100fd57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610310945050505050565b005b61013b6004803603602081101561015357600080fd5b503561036d565b6101626104b6565b60408051918252519081900360200190f35b61017c6104cd565b604080516001600160a01b039092168252519081900360200190f35b61017c6104dc565b61013b600480360360608110156101b657600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156101e657600080fd5b8201836020820111156101f857600080fd5b8035906020019184602083028401116401000000008311171561021a57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506104e3945050505050565b61013b6004803603602081101561026e57600080fd5b50356001600160a01b0316610645565b6102a46004803603602081101561029457600080fd5b50356001600160a01b0316610714565b604080519115158252519081900360200190f35b6102c0610732565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156102fc5781810151838201526020016102e4565b505050509050019250505060405180910390f35b6000546001600160a01b03163314610361576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b61036a81610794565b50565b600054600160a01b900467ffffffffffffffff168114156103d5576040805162461bcd60e51b815260206004820152601860248201527f74696d656c6f636b203d3d205f6e657754696d656c6f636b0000000000000000604482015290519081900360640190fd5b6301e133808110610422576040805162461bcd60e51b815260206004820152601260248201527174696d656c6f636b20746f6f206c6172676560701b604482015290519081900360640190fd5b3341148015610433575041620dead0145b1561036a5760005460408051428152600160a01b90920467ffffffffffffffff166020830152818101839052517feb86fa0729fdcf66bda3d834e93bf513d3740be7f7a4a6cab0dd318f1df8514f916060908290030190a16000805467ffffffffffffffff8316600160a01b0267ffffffffffffffff60a01b1990911617905550565b600054600160a01b900467ffffffffffffffff1690565b6000546001600160a01b031690565b620dead081565b3373fffec6c83c8bf5c3f4ae0ccf8c45ce20e4560bd71461054b576040805162461bcd60e51b815260206004820152601760248201527f6f6e6c792067656e6573697320676f7665726e616e6365000000000000000000604482015290519081900360640190fd5b600054600160e01b900460ff16156105a0576040805162461bcd60e51b8152602060048201526013602482015272185b1c9958591e481a5b9a5d1a585b1a5cd959606a1b604482015290519081900360640190fd5b6301e1338082106105ed576040805162461bcd60e51b815260206004820152601260248201527174696d656c6f636b20746f6f206c6172676560701b604482015290519081900360640190fd5b60008054600160e01b60ff60e01b19909116176001600160a01b0319166001600160a01b0385161767ffffffffffffffff60a01b1916600160a01b67ffffffffffffffff85160217905561064081610794565b505050565b6000546001600160a01b03828116911614156106925760405162461bcd60e51b815260040180806020018281038252602381526020018061098d6023913960400191505060405180910390fd5b33411480156106a3575041620dead0145b1561036a57600054604080514281526001600160a01b03928316602082015291831682820152517f7e1a30031de5a45b59b70d6a9f61956645cf3cf9468588f31f4217f7c770d7cc9181900360600190a1600080546001600160a01b0383166001600160a01b031990911617905550565b6001600160a01b031660009081526002602052604090205460ff1690565b6060600180548060200260200160405190810160405280929190818152602001828054801561078a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161076c575b5050505050905090565b7fa2c44af5dca41c60e42e3fc93e9fc4dd6e5d2c14ededf08259d3372874ac085442600183604051808481526020018060200180602001838103835285818154815260200191508054801561081257602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116107f4575b50508381038252845181528451602091820191808701910280838360005b83811015610848578181015183820152602001610830565b505050509050019550505050505060405180910390a15b600154156108e6576000600260006001808080549050038154811061088057fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff191691151591909117905560018054806108bf57fe5b600082815260209020810160001990810180546001600160a01b031916905501905561085f565b60005b815181101561098857600182828151811061090057fe5b60209081029190910181015182546001808201855560009485529284200180546001600160a01b0319166001600160a01b039092169190911790558351909160029185908590811061094e57fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff19169115159190911790556001016108e9565b505056fe676f7665726e616e636541646472657373203d3d205f6e6577476f7665726e616e6365a2646970667358221220dc35724b5a2e5b003d9e6cf36ed995b5398fa1e1ef57af93db7adb9451b6d5c664736f6c63430007060033\"},\"0x1000000000000000000000000000000000000008\":{\"balance\":\"0x0\",\"code\":\"0x608060405234801561001057600080fd5b50600436106100415760003560e01c80637d1f9946146100465780637ff6faa614610050578063f8227a9814610084575b600080fd5b61004e6100a4565b005b610058610193565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61008c61019a565b60405180821515815260200191505060405180910390f35b60008054906101000a900460ff1615610108576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806101ac6030913960400191505060405180910390fd5b4173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480156101715750620dead273ffffffffffffffffffffffffffffffffffffffff164173ffffffffffffffffffffffffffffffffffffffff16145b156101915760016000806101000a81548160ff0219169083151502179055505b565b620dead281565b60008054906101000a900460ff168156fe496e697469616c41697264726f70206164647265737320686173206265656e20616c7265616479206368616e6765642ea2646970667358221220c906bf054cb3344ab797ac0dbc06b2cf8141f2972eb3fcac8520708eea95327664736f6c63430007060033\"},\"0x1000000000000000000000000000000000000009\":{\"balance\":\"0x0\",\"code\":\"0x608060405234801561001057600080fd5b50600436106100415760003560e01c80635ace4f0d146100465780637ff6faa614610050578063f5b2e3ba14610084575b600080fd5b61004e6100a4565b005b610058610193565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61008c61019a565b60405180821515815260200191505060405180910390f35b60008054906101000a900460ff1615610108576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e8152602001806101ac602e913960400191505060405180910390fd5b4173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480156101715750620dead373ffffffffffffffffffffffffffffffffffffffff164173ffffffffffffffffffffffffffffffffffffffff16145b156101915760016000806101000a81548160ff0219169083151502179055505b565b620dead381565b60008054906101000a900460ff168156fe446973747269627574696f6e206164647265737320686173206265656e20616c7265616479206368616e6765642ea2646970667358221220c5210379f24633bfce442461942f2fddfda5f948c03018584a4506807bd3fa3564736f6c63430007060033\"},\"ff50eF6F4b0568493175defa3655b10d68Bf41FB\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"ff898D83DE2F1E07ad44f9Ff34bB1ABDBCfcBB00\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"ff31f7568813E69991fAeCA13907141cc4874723\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"ffF9AcF70B7aFaFAe6C495aEEDC0eD5B0EF4011e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"ff89975844E384a1798b0cD24D7611F44Dd17040\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"ff65397290C660596bFf1564E333f870E577F4D2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"ff57CaF5B871db64F2a7F4C5bc2d17A5E666F7E8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"ffC11262622D5069aBad729efe84a95C169d9c06\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"fffEc6C83c8BF5c3F4AE0cCF8c45CE20E4560BD7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc783df8a850f42e7f7e57013759c285caa701eb6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xead9c93b79ae7c1591b1fb5323bd777e86e150d4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe5904695748fe4a84b40b3fc79de2277660bd1d3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x92561f28ec438ee9831d00d1d59fbdc981b762b2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2ffd013aaa7b5a7da93336c2251075202b33fb2b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9fc9c2dfba3b6cf204c37a5f690619772b926e39\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfbc51a9582d031f2ceaad3959256596c5d3a5468\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x84fae3d3cba24a97817b2a18c2421d462dbbce9f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfa3bdc8709226da0da13a4d904c8b66f16c3c8ba\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c365935ca8710200c7595f0a72eb6023a7706cd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd7de703d9bbc4602242d0f3149e5ffcd30eb3adf\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x532792b73c0c6e7565912e7039c59986f7e1dd1f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xea960515f8b4c237730f028cbacf0a28e7f45de0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3d91185a02774c70287f6c74dd26d13dfb58ff16\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5585738127d12542a8fd6c71c19d2e4cecdab08a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0e0b5a3f244686cf9e7811754379b9114d42f78b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x704cf59b16fd50efd575342b46ce9c5e07076a4a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0a057a7172d0466aef80976d7e8c80647dfd35e3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x68dfc526037e9030c8f813d014919cc89e7d4d74\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x26c43a1d431a4e5ee86cd55ed7ef9edf3641e901\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x23ffe739ebb7cd7f736c1e428b93181429afc395\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x650240a1f1024fe55e6f2ed56679ab430e338581\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb6ee2a471ff933a9a8b16352386665f2d0121dd3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x856571e47e17a5b6a6353f8a4483eb49946e5697\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x26a90cc1e44ffc533f49401c1f2269a6080e4e21\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2e3bff5d8f20fdb941adc794f9bf3dea0416988f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x90f97f8367aa1ce1c3314dbf4ff1c9419b113e0a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x19321ca3a6784186ddba167d4f8d167f46dbe0db\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf7ab7b05822d1038f30080cc4a4f27267d8a852c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc30f8ecb6d246af5fd385f00e2004215d78e29e6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x447dcbcab6b56f80da53b93050e7d0c9cb3d2a63\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x37f900870430a080987015f3a36c6c65ed2c4d55\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x42d889d00b9b853dbb9b77e074fcb65c1b46d35f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3769119b83c2f53c182a4f725917065179795542\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2cae9302f38b62425eebfe83fe78d49d6f2f8707\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd5147e4f21385355c2c4735830f716cd5a50f778\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xba2ac417f2d878c48028400b26fec3ecc0adf8a3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4f6e59dd6186aca90bb15ae6e4993bc3a570c013\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9e32c568279a54874ef0dcad4f0e34f04c34ea45\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcbb8f758007062ad35073d306a89216917dc370b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x20a236b2ad0f6fff7702cf4ceec77b17ae140d2f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfce184e61f4951516b07a060d480976d4b43f20a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0f390d58fa98ee9e127750cd7e3b76a3cafa47da\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x13d31df6059080ed4e67150b1825d882a7cafb7c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x228d7f657b958c361f348d74213750c4ce62e19c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2513e65a65feb610adf8efc0a30179adac17e33f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfa1d546f59258d144cf4d53b33eacea7dc368edb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdc5e866f6dd7e1a69eb807e1adf445dae895b3b0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x17962bbb6dbabf7368b23c719d2dc96ca8cd1269\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd6389341db0009b23b565e54fbb985bdf0c5de71\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4e022a3735ac9d3191cc9221aa2df2e1c796efba\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7d9d5e84670e44460c9ab8cbc2ff7b7610932382\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7f912de764e68e9205adfbc309498153c3336376\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x520a92cc51a29add12bd4c4af72f651a11398f85\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5bd95b0ffaf0c5c255b92c7492b709cd98ef2bb0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5b0e445c3eeaea758d37950b7f99eb1fe3e6277d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x99184c49afe527bf483074a9e598f561805b513f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x09940fb83d71e70f30e39efec83053d7d9346df8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x42e3ad34e59bc7f1b8f020be535f7a4a76c2a7b4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x289a2d8699c558af5bb4cd50575e018e3cf251ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe55110392d061b40219f8bf79f3273a95291a172\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2ed99292b9592df81d4f06bc238d6218f5c9836b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe7385441f3785ca377bacacf908cd0b9b7d8bfc4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1689a35d11dc26079605776c8eb343242ba2c28d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf594c05787a0d23b1135b23bc590292c6fca4081\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6299348ff3b9e7cdd5a32ef81a2f87cc68c794df\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe5d33c4b3b8e516c83f27151bead5787e6a1f80d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x087313eec041a19d94d1ef241434d594a462e2e4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb99e9d73d5d0abb703874a60d844c3cbbe22e098\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9de259527c97f7c156e19d3dd0180601faebebda\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x561b3214777c7ef68934917aaf223fb1119c745a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x157393968b7c2d01202aca1f35ccf46049f9d3e8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x20e31bf696b7349c3ec5c0e732ad4920aacf1d3e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x44362bcf6ea3db06e5f3116e1eef0e2afd63e14d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd228cdd43cba10e9c98f380fa0273973dec2c955\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa89ca00db07d314ee792d0e1f241595ea111292a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8f38dbbc3e3e3a8b003ee505b0d58da17c3ec617\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x92661785b3d8c41b59e7a9bef38996cb10c27053\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x45179fde9f7049b6d8a942fa5fb2c40003809733\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f4679176faf6edb547439c3e3f767acd916e6dc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x395b7eea1f71a052c549d5a2b9f5890c773a0e7c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xedcdc766aa7dbb84004428ee0d35075375270e9b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd5ac30b45b404dcbce34c713f1005522760b91a1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x999f9880ace3752eb49caeea01f247075ee60d6b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdca1dc39ce877567d49cb907e37c7cd19116f8ef\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x85e6a34e900d901a40820843d3ff11b44e5374ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9577e69ca21b8b5eab153ffca33e04e064f2745f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf96ceff99becc79dd9645f3b7f60e7bc8c34ed19\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xab8fdff7ce6e33d4ab6959e3130aa46caca2c128\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd028d88b4f26227fdc550c387c01bf89f8c83cbe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x172e45210d8aac16d4196529640bc702eceaf5d1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb77c202ea30e6b8e3bbed342d45348f629ca1dba\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x24906b3dc5e4d3b120fb7c01a2771d8cba3732b2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xee0411b166b4ac0f9e995fb1b8a5e21add6b6752\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x74204da71ed7537fc8f8150cfce9f6a5664819b4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb452ed1fabb6502ade6ac464f17b74553a47946f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x74923cc7164bdcdff7a4147ebf8d847a119c5610\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf84d636b208aace4ab706469bc40a31ae61ff2ef\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc50702300ccb1aa80ef8353d9135c5e47b45453c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1267805e1a92f46901d0d7a60630482b8a59240b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8ec9813cdfc80fe7fc24325cbba7c8258756d2ca\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xee935da47bb2bf1c8be75f37f1a9512d09dc9459\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x52ea8ef624dedad1181eced8913d95745c1293c2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6a0970d7beb061889bbd4b39884e09000391d248\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8ccd9f923ac2ddf03b024cef2df8830cf9d4fca3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8f7f18b96e2f310098789c25be303bc0b24e3002\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8b4983e6621c48a45ddf9630aa732819f27c86bc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8a3019f2c85398609ba83fb90cb7eea1416512e4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3201f55a5580c8c6e3d8d166be80415d0bb3801d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfc98e3cb5803a99247fe54a77d2181d8d14fa901\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xaef9bb7375496d40df484223a51a330400434705\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbfe5618099dde3080da6ee0df4d8d07cbbae0037\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xaf1903f7d42af0e99fff4332af76824d98f7e63a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd9a5437a8110995cedc79f3a4f2c05844d4eaa3d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xccdbded25ee3dc4ec97fbba976e8f3e5df5c72b3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xffebfedde0c90db9ea739c40c0b509591afd662b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1425983077c306fce4291e09d089656ccf844995\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6e4294b48a3c4762cd3b7ab69716965e75c273af\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x44ae55919480f37a87f7af00f990e0990f305219\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xee23fdbd4ab43b19e1816545cdce0de2adef71aa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa1072b8464aeb4b9bc8967250d95c8c622f28598\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x33f31d2a00431fc634e7439aefe8071aeb4125d9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf71c03dbb50f54dbb487526268a94a3e6a4647d7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc4fab9daac9d1045f6e401efd10d4be2bbe4df03\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdaf1f85c5180b24209d2ed9761a1aba708bd0cf8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd7504e72fa848e576a28603a6e016985b40253ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8636fe2db78859fe9ba8872829aeb9d49da57328\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x76ae032f4e84e535b76825d48b2244c80d6add3d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc14bcd373c2d17b264c32a5c3d5e2b2a4c3725b2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2393b14cd3b76a6729bfa11071d6d477a8543c56\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3e6d22a63f40dbe2a97e2ff21746f1f5ac10e975\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x05a3b4382a28cfc4daff2b8b79eddc62bb6d3124\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6ede5c082dfbf511331d1c984709b5c2476c8f5c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5b6618f8729e74acb74d2fe2e65755f5312e95e5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4d430e4bf3603696890799af3588fe8a8c57e4ed\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5d7b288e0e5140d480a505f412b3337a62faa126\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x09ac4613c261a678e436a009617a07ee86edf26f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4c424e8ffb8f562bf7d5f3b3f64b523354a6ec81\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf40a227706a848a09ba013382db7662ac3adf723\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa95fc6fea88e2bbf7070462a9b3f85445aefa0df\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xaa643da998a40da88bc7bbede01182f3e651c13c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfb6adaa2915843ca3a8e80304469cc1286ea3a1c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1a17d722a71d75f4e6d600041ccba90d29c8b1f3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfa2183af46b7a8317f0c2f568cd131b912ae9e93\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd0131729628315ceeb65a6bbcfec4dd2a4bb2810\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xda405bf4ee57f95db80901c7f45b0172909494ba\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9d80a2d9417a52732b3543ef0c895c36c38a80ff\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x95bbab624d7fb54e8b0d417ccea0dff4b52639fe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x17498781497cb4ed7f94698c6f7a8dd4ed908e12\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c5acff73bce48f4cacf5565d8f18392b0fff3c1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9a34f81afcafcea2de06e9229f4ab81b68dd15ca\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc67164f40a75b1aa802ad634c7eb58538f00ac3e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xef8dd0eb060af5dfa793bc4fb1ba1d6ba1106eea\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x60948c1da9d62187e7e72f89b1e5bfb788f084ce\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5bbc3d63d5f1180bfb2a4286d3211bf4cf175057\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8d8534af5ca90cd0930d1e560939709ba87d5919\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2c21a2f6f0f2f1dd48403f4fe41fd3d7d711fe8e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xecd4b1bca600029d2e742d5098d5878555c6c560\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb1284b2c389d8bf6348c7a02ef38e588d4e67ef8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5ed3a020f43df27f3e59b4c743afebd92bc46202\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x837b8103ff0541ea8ebdd21ef6a5f2f7a0df5ae6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd7fba4a5aa18713a6afb5b6f9ad4c317e6456a2e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x04940dc90057c056559633e41c35012b26fddd8a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9eeee4fd7574c56bfd589e8dc08c470f3b431633\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3778d4516d12389194e8685a265fa7f5e21ecd42\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6013e8aaca9a5c37ba52610d4bb5a3bda16a55d3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2f9b13237038afa70fac4d5922f77b3e514f9f19\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9a39cbb2871146ffd5d05a848de94c59bb659e9e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9a1e43c547f29bb5eac09b98fb43e89f23f57e8b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6cf14909d42efd1de11be9763bdea9268c89c668\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x31fb5e464ca8c9c6c3ee5b6a839e86a35d56384e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4d3460db855d5c93052e7916810d63d15e56b7f2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3524a66a2bd2b06c94e568f8dd0d887e00c57629\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4458926f52c0322917d04406892afabe758db3b2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x149ec481f0ad4363d32ee66f2b232f4e259f394d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xabc9efd85fc0d189cdf88e17c6d5afb9e6bcd232\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7dc755ebe8b8ac95b08b9b0b1f40c683503e78bb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe2ab648b877b4196d1916061ec82ea5935577f12\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd58cd1ce482bff11f58135edf971138ac82edfb6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2388773c4974e051fc85144afc66883b61c242ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x31ea79bc8d4fa55f8528704464c2286adb07a06e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x103ff70897d2a966bd2f69783fecebfbcecc9486\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa3095e418101d10a5e63ebf47166f3ce28cf7959\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb3045658685e7789a9a1a81988187d47b42b2335\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc6656d75039a8b1b4e6846454c61c0fa4ceca3f3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa8be0fea56c43bfc4d95e5a6e96dd4a5969efff2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x29fbb69677fcb4c7aad675159647a07d2dda1a1a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x825070f7f6b1099c8e4238950b727ae290db31c4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x818f9278f77451f9701f09707f345c6751b5ab66\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x494f53218404526a8feb5713fddf491f1222c0de\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x29e8cbcccc737c36a03edf4d2e14ba336e0e2c3f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x06d9c2ea1eacda50e806dbe66c46dc61d3a6e3d3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0a336792cbfee34a85653ed6edb0bb97bf998d9d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x844719e597b9d0fb038905f262626f44ea925862\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4f9aca238f29097666c2f50ef19ef15e1ab1f45c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb7b56063b28fa014446121da7c54ab642ce17e7a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x531007ef24ffdd3f6759175288dc19e76536016b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xebfd40fe4411606120b89b6d1e4b3879573c6235\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8ae391fe43f833cd476f69a225ef15cad261c8a6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7318a8cc3589c99642933c2e737ff97301b91383\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x48ae239526f49a3db40b0e98c08b6c5971da51cf\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc1116c985757e65b5aadfc4b596b4feef00962f4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x09d141472dfce60a54515e43307074f8ac931b88\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9837828bde2b8960012c190d3f28a64b07e58dcf\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe65c0ec865c9062bfcd8f68698aee5af5ee71c43\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa75ec064b9e2939f7da1feb4babb390b9677facb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf3a94b5453a910c60af2e9f38673e833270106ab\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x896602a6f91136a8ebc921cbb2c6943ab6961002\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x622c94affe2c3a30da80998434ab75c33877f4bf\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x681009434cd22ec4c4658235edb6f4a7421f1b53\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xea110becaeca3898921cc118f923f30094b183a4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd1771ff76a0cc182da1ddb999ad3ab79f4a374af\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb55c0e5da385a64be1f44ee360da122541e2e6f6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc267540bcac9fe4f745c2dad5cc361f6cc5d5d6b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x636fa2d6ecf4769d57028421e14181d16ca18729\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x531d5f8aed6581bdf430067164d750bc534cb308\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x55affaeccc2f6c92efecbdb89fb4a314300e7fdd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa2f0460b16da4216ac763bd599d489d29a9a5936\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1b40f5c11bead10d257198e9e32f4596c531af25\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd75cc0ab2a9522c1959cac75ae6fe8b5fbac36d5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x21decfc9b16284f7cd584b3abddd2ddc40cc3e1b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8cab06b83d3ef05e1328e2235e54428e9731c1de\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x817a85d19af38df4903b327882ccfe2637f2f7f9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc28c7358c05049cd63ce5855148a993a4c262c8d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5a1ed9293cbb829b6e7c479863221469473504d6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9c47b4f74a3c0c8abe44a75fab233d74f400c185\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3da3a23c4bb36118aa98feb03dd357d5a0081149\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb504c5dce116ffc75e279438b26ed1cbc7b3bc0a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x960f9e968e3d5c085b103ec353ca8f33066172ed\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf29e6fb8cf722c55bf01930f6aabe68c4017e532\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xef41a6917fb9c5364c239b99f013345dd0a6219a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x30cadc8b9d78bdf07afb3740e9e430d290c1b3dc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd30d45a10a783ad5def435f38cab246a2fcc84c1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa5d0ff7f34b1eb12303c2ef31fa90a87fcb64be6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x563f90c4e220b34912b38952e781b9d424634adb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc4082a2b6d1ac2e945021a711c593247d85f1ab1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb87831bee032affe884158fedd25df858b048d97\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7ed972faacc55171885c3f9382bb4c9bd47607ec\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4457b0d1ce4175d13ec2e1ed6e9c03015b66072b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7b1c26bb767ef6b8cba4139a8f8eec045b087001\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbf46093a85b1221855b7d757e1f153db1d71cfbe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c605eed5c057c5950426c969e8d0026d455edfb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd291ed07fdb95df18c267fefa8d8c30719bdb996\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xab95ee035b194fe2020c6b53c94fa62a6e120ab7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdd2366fd99aaee2d933a0c158a052dc924026a3c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3ab2b7f5333969b4803b97638e7263d08a58479f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x19377255a8f49e1c5af37f9ee81e12127428aeca\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2b70cc7d7652675d7223d475ba7aa93e8794f8e3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc1ad1de12d77dc7a9e22b9a4bda0eb6eb77dda1e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x705013b60c59c0e4cbaeed8e90192109c499daa9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1bc1a923a7a326542d507013b2ea6e6277e2aca7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2b5ea993766ff41beddc8a5310f8e5445663037e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x398eab9d416d647038501654d29cdff8a585e532\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc623c3c2458e4217fa212c76a0ac82165ae6b696\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1649e1b2337087c0626f3f3fe7befb404b82f234\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8a8daff227d30acce694e293ed58819154ecb3a3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x878d1313789f787014efe5c751b3b1f7a1bb454f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x147d72bf17d179b024a9e54bc7dc721720d1a78c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1c0917222642f72ff0885c72eb01362b29e3d764\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x47ca1e20d8e33cca95c3fd91ae0244e5fc6cd829\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa62ca2207a5068f1cf714127737227b10e149ea4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdd072cdaf124d06e41bb0cda4cb1d2b88bbcc71b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb241a20dae31d2e84725ccda6b4d9b1bfa346ba6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe78b5ec75a2371cd6ef1e5625d5a1c93a55c5de6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xacdb46238d915b6bba26651ec88f5b41ec76e282\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcf6f0b6ec7352174b7fdf3a87baa66d827f033aa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa49d8f0134ac7b8de656c6f76cdc525786970c62\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe0cd75931d4e5947f25e645d3917a6be2d9293cb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x819379fd297df0ed76a274a6f44b77885d13fb13\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x057cf47f73d3646f048bcd80788d0a8f468bd518\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x969b00a6a83d512c5288aa2f75c4fe7e7cc693b4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2a5bdaa4a2ee52a66adbe0fe23e2ccf7d529814f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd39ded7d3a5b2d3f2608358f339ffb2f274217a8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5cfefa692321e8e78b3c131133139e7702756b9a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x967dec44bff01a319cc877e466f03cb7a4af1606\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xea8601dd6537420934a3b49e97ffa45be0c641a1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd300023ce15203a494e2292e48b4829140023c52\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xba960169acb1f0c932bb1019ef51cb12fd6f7d6a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd6c2d7553583802edb6cc0e97e0991ae410b69ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcf46f63b2ac90a37801d4a59b4acd5c0d382821e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xeb093af59da26d6432a8acd5e496d27d4edb44a7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c278c8ffbbdf505e4f9afbc59b9f33d4da3844a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb0201cbbd8e74a13a0465c34989a222180b8d194\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x56f174ecad22195e25bcb9e95e6451cc19a5c25b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8f23a8ad84187d587982585259875ab008a8704d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xad9d4567852ebeefaf2f7feea6640f44cdefbd9f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x694c471a06585d3e6f11b3837968c890676f6518\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5549e1db9ded2b74a4bb0073f496c00d606abdf3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0b1ef77c2060df2b2ca9818f22304d19e1cb0391\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x154eee19cc5f99eee951d6a8f0e47d46981b7b13\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x58677ad589d7ed0465401a20f6f21951caca43d2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2dd1fb20aa87db551f67b6201141957089043851\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x252ea5735a4b117eb143b0be1550996a3798631d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5cfecdbfaf34e82698548308c3e0ec7eedfef181\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfb8471690d0d4ee898e6fbf8e0cd2fca39b4793a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4a258e545254b339ca654c11ab79b22f0a7a84ea\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1ba5998ed713bf298e76eaf17f98f13f79b76c1a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xac7f6bbb17fc8df8be088333002a2598175bdc5f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xed1a4f6fa392843697aefe0b68c1d709a2e0e35d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3bbc7916c6dc16e1fe9aba9f63f83b1531e3d696\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe1b0148a43d977761c2c9255f1249aed4c0d0e7b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbe4188324694e7ec1946d0f7699afbbc31a2a628\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c559a8d3aff9051f5f57ab1407070aa61086532\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6620459ae16647d714513a044558abcde12bef78\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe31e9557ff002cd8d57e915571410da20a0bcbd2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb6dc5c9b738de0fe5e94543ba0c9311d2a5c599a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf78633efa2ed9a983866ff1eee3eab56835867b7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd0e115400175e1c2cf00172478aa790f8fd7ce1e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x356daab68218cbd3aecdbd32a5068451e152d6e4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x43c748e8ad85d47b4aaa3cc6ea1f349444fab587\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x88a054838aa1addc9e77a18b476ec956f8e84fdb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x35eb42497fc314dacc69527f7aa7ec9d71cd56b1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb6c1947d4526b0c9e5691379ea866948f8096a11\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf1e0cbe283f3a95d3aee30d4a850a7ea11398565\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2ba8ebfbfabee0453becab43aa56fcad2c200153\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7f83d4cd33d3cf6415de8fd46790c5d4b4372bfb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4a4cf61df7bad1660dfabf632506e68e26f48b80\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x25bb179b17902ccd43034a6544b9aa5580be255e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1ffcc5a9b0fa320172616d02467a0f68be5a4724\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbd51b8840b59f0c130a68edafc3745e41625c03d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf7157477902fa49f648c497ce5aaeef8cf287738\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf1f649f8978840e5df380b34d67676cff319f6cb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc97816df28e58c478a08038610317ec35bff51dd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8825cf11b25d0642a7cbf0192ac495bad40b52e9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x28eca76dfdae4151f1bf7fc0e5203b0d84547693\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc6ed21b23053035f616f2c2c8a8c3fe02ac536f7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7bd792e4a682bca2b874c2c2d7d669f506ff8bf6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8cf4a329531d95cf3039e1bd27d70b2988165831\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x09a5fd2b840caf1a2c8006e90858968749a08605\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x463cb9f954f155273f9ccf08ded5b47bf749f2fa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6befd88d6b8754c09f3e9720a77c438f8bb8e14e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4832601c0f1da25f67bcdbb1168727a31a67611c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x847bce8d6b81589032147ffd37bfca555894de24\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x235ea581aa51136b090cbe7f59c7c480ff4067b9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf9313393a8e010f4c7d8c7a454fe83ba1e106f68\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x44ce46beb99f105c3d935243262b76aecc937392\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd82b0b673a285def5fa1f1011e6fb716ccf43244\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9d9b1ed165f01d27d0c5bb1534177a1fc5003ce7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3e8489dc02916c4b3ee39e808251062b40ec20f8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x05332acf904f86c040e2076065c42e9de2ec4867\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0ea1ac52e8d271995893e2940ad3546242bf0afe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbbc14cbd386ca3e6928e4a1ec4b5040cab7b422b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x08f8b4c35f9bf9d4f704715d869bcfa605728c37\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x40ff1280e20bed8d7bf0f9c7253d40ec9f4ca5d9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbec7dfe29e0b7a663d137daa38824347d3c7e7f6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6abe3dedfb0e4746ce26d0b2fe126588ee6b37be\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8225da7f0b3e719d213acd260992b21b076ceb63\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9a3721112320b03d087e61d36a3100eef954db5f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9986aea413ac15c1a9db714b6a74024731ce1132\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfde1529f5dea42859ac5c458d46ea67bf0958980\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2cd129f2af1e0eb7e8ae0ad06efdad449bd04030\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd0b3940c889f9c6c8de81e85b9ba701cb24c632e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb03a55f0581712085dbbd29584e891e5b53d94a3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x47fdcd05315e8cd03dbf736fa42dae83375f3d4d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x16f27075a76bae892bfdfa1490e20c9ca3734430\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd71a3e7d6e207418e5194c27c224cd2cc37934e6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1f92e89a280472be72d72e690328844aadfebabe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0e33b5400eb83f5490769922f9c67b7ac74fc1a7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcd742d9f463ea741f2c295ecdabe720d2c28379a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x696ea71c93d53ffd630d26026c2e72252081ec88\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x22d36c42c9a7fe1200fccd59d9bfeca3e36e9283\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa21383aaa2779d95acebb51d7c9b874e2046929e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x665c0e202667aa73a379d9a85f86f523a8de2a9a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x65ad1b4c2529436df045bf4405efb4657820d79a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x02dd448bbd3aad0224a87beaf5848501f7116de9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x72b87f033c16b597b00b47df01ad59408fea3afb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x862f9fe64c5caf03a13cea9b7313baf98c5418b4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf34612dcfed6f0a47119d8068098fcd83c9f5229\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcb6a29ca66828ebfd21114e5e3e7e224b26d17f8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf25d17a3a7ccae8864ce2bad7ff53b5e5eb2d3a5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f2833c83d48dcb51fae40a13bbafcd96b874255\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x74e9259a6ace209f7862c5defa2e541a7506ec20\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8bcd984f86eb5f4f7fe23e2b275a52a7e0a550df\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdec65d5df11752c2507068c3a05f58f9c30d36ce\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcdbb689dbaeb012cc5693cc12ca394cd93dc1434\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x443711c422f80030b7ea9f1b42f0127f4ab3245e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6019bf8fb4eb27699616b7dbd5c824ceb08dd37e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x284ed985f94071865377902c57ed15839e4d47c5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x468f89e7b93194a4ef76f9c518c5c99bf703adc3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3b67efc7030510c218005f0e6ceedbd145447fb5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0269581b33eb83df5a33f0aca374d990257c13a5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1b499d79c55a44f0082110cb0e1fdaf15d94e939\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4b3194860112a840e35599a6246a5fd1e117d7c6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xea1a04b55b1ba87dac18ec1f8a76ba95b0378d9f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x73e80b736a19b10a2ea16e1a3ff54f9de01cf06e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa075b958ad232480f081e490a10ae6fd781f1f54\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x44f2f94dbdfb67bd1f9f7ab5b5fc3524a9a37596\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x866f69fb7d36124d2a22d2e09451e79b9a8c1922\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0c9abb93682a0baafcd16dc20f5617dbc499cdec\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x767ac3110c612b332d0524ecb9ab24839f3a71e2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6e42d5c1df423941e4da984d3b44efd2498a86a6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2705b4966f4d52b77b4abfbc969d4089f34a47db\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xebb2e83cf6ea0ffc18ceec7cd3910edde2e83e11\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xec0eabadfd4b473224b3ea4ea1396e27c3def725\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa810e8fff39e7270a4c93058b75788055bab84f6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x63220c8d229f1763ba97b711d1fed7cdbe78acbc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2723eb21dbd539dd3741cfb82250679241edb120\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8c0c9030a91c1db91b90c30b85e529d285af3ae5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6ebbf3be6f31fc0437bd14aa0c14c357cd2fc864\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x890a5ed079c0ef6dee70f43ab7829466f81f93cb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xefa752bc4100400d2e0c0a13bbdb0be06b7cc287\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x38980757e0bebcd9a371de025a18b8db517f6b8a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x32d5b114575167fd8222d4b331c54e647bec20cc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x70067edf7ebbf612834278988f390a6a070bbe97\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7bf69f53e0b8e884e279e14b2b40d3ef26d9246a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1f52aab99696244103146d783c62419d8db52bc2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x86a010f6ec977d603d71e55b6bada74bbb7b9672\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2c0289cf407cb48a2bb2837095ad53f956eeb334\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6350192329a4a3cd50db2e378a07bcd59beb8234\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7743b07b17a218cda814ac698b5770623902b3dc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfd18e536fd8daa35c21dff0ff0213702fb385be2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5658e20e19d3fae07f80c1af55e1a488e24e0a3c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x987e8a64ef3ed853df0fd9aeb61cb2313f5f2e4b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x52f9fc7ef6c8108f565b1b7853b666f645b48c42\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2f1a18143b1dc39c3ae7182fd6f85ddc2e69356a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x360fc70e64aaf7f230133e42801ee7a08a1e9329\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x78c8027d4cf1d0b9f80c5c60e75c887a68d95689\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x92665b827d19722a9cdbf6b62a054728a01a8ad4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x24432107f1ca9c3e2cacace13d7839e3bcdbea24\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f7e6c66bf9ef2493ae244f315434a23ab02958a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x66d89c9c8f7520663ce878a1330fc0cb92061d1a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc63190813e47ca081f529fa0b2e8c99c8639fb5f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x98cc860034b2e84873c1f848162715cca3024700\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x025c5e44261a4c1dd25409adf2368572dd594f0f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0be35e76c165082acdeab7fc5cdb0bcdcc8db167\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9c8f619ffcc2d7dae1a337a532c64023c80f2633\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa8e0cf2178f1398dc7bb4c29a11b1bbd0aeb6393\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x93e95d0913963a433ca6730f035d81e5a6adbffd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x98ddd07ebfca0ff85add3134a7f24aefeab60a0e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xad966d350f18e8c561b99fc5336a9e8a395c1bbf\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc153ad3b419e3622fc16f57fa92399d85e119937\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x553956a741c97b624d2e9df182d82607baee9bc9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5fdd8d87d18cac06c4531f8f47bb55d21e7c0f02\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x929abad38ae562858a6754060d63bfcb2e3d51fe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe97a06b3952c8c515b21c5c260190b017b5b4fc5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x45caf7332098b7dbefdb4435d646cac0c12edfca\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x967e811271578443b52469bf3f76072136cd6ca4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9e19bed4d1c213a84ee0e5e6b1b1f9a001008188\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x517bd751e077e7b0ca04f89cfb85d2ccc3a14281\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8541d2cd95925f44f39c818a31564aca6ebc71de\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xaf4e214b0d636d22239e4ba4c4d19b21f366d5e6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb795855048b34a4fef4c3a589021eb4953fd8b27\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2a7564119ff4e3b4dde4545490b9cf096b6179ee\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf1124518eb0297e5194880ad6ba3fa10eb6064d2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfad5bda11a42baba893cdf7e4e2d104828f6122c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x660e80f1020aae7281008edc64831753a0043734\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf04b1b41cc92e25a17dbc5c587979b32cb14ad0e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6ec2dc3c714cb122791a9145c18e5e7e90ead89d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3a2eaad7b568b82117047c90c1fd78b3bf6fd806\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf079cb18614a26df1d2a929db53339e101319329\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xeed9842433eed39cdba35725a2e29e44bb053399\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb32f130c7ee18e1fe166b00ca11164eae2051a8b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x06078a9ffeea8caecd45631d67aa489a71b1f24c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xef25ddeef2483ce4b2273d38d656aecbd5a77485\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x340a5951bdc42712fc1eca90890539b5529b9e07\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc262ca9892d6275056474f07fdf692d30221c8d1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8fac8c9b163e21b4294a5beab01c9817d8bfda76\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb8d4b22c829b630a9ea506a581ff7b6e607f5260\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x03218129eb0f8ee840138cdc1d350283b61d6ec3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa8cb09d4385636c9a49fcc1663550fa61c938a18\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x33383f6a7d83a6307b35edfdfa816b4d57ee726d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c431c4e05d3c914219370bf94375384e16b03ba\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4eadc983623f7d686388539c3296d283bbb5b2f1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1fe570f5aeecd3b2683984000d84b1e59868979d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2ed589afd0e744f96ae0b74a72a9004992f5bc71\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3d4923cec22fca7cc2545218d3eaee17297d9068\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x46e3ed71f57fd0e99803e5fb78ccb0d06604935e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x734bebe1d26894ec495b445c85d61b07093027a9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7b3513df8bcde1e1f10322d623426de7f7ecf18a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9a881bf67979cd01a9f4d9143233b868760af094\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe0973e6be85eee8e37c6df823330af91521c39ed\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5013a8349eb02fd299732e7c417a975b203d5b56\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2d34ec9ebf98362b94b1867037b9ee3a9903272b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb9a465a44da2b8815fe3f6b736d43307bdd96c37\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd26bb343d60ca0a817af4b46e044d50c384a73a2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb4fa78b0321369a87c361799a1cf4eac30bcf04b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x84dc41c44b925604330dd02adb62371824063d67\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9d5870e0b1d0e8194a99d0d7ccebdb6a1eb7563d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5c3f6074c1b17d96b98884b3758d3553873262a4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbc065fb9ea26a300fcab623f7410792df3505eaa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x688581e157b5cc3309f409a676dfeb8dc22da719\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd8490b905d579951bca37926c3f4b60b3b60177c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc674808df1025555e38bfa2f7ae7cbcf57a5d7d4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x63c552dab9f499b44cea15169626e946a6141e02\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x86d814297a6b649b6e6dbd265c92cb7a038e9f13\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f3b96880d666988c2c39b7ef10db3a2c6965cb5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x52111f88e3fdd827ffbdcf8f6e077e58b424069e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9fdec9f825c41a7692ff31c66770cf95d3143e2a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x600cee7cca9e47641934d10e3a6eec10e4c276bc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc86327ad45daf8c7646e149505d8f203c5606638\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x53cbbaa3b85aaf3ceb6ba880cd3206d57ca00ea6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6f140e3adb9468e721aa940ccf5c0379b717b8f5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9512b0360ddf41ae65993742f2171cc81597dc31\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8ef26f157fc9d1ddc15a78697e3b111ba8842811\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8bf08e3facb66301934c6eee45bf5e99ab7f317a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcf99327ce80ad3987cb2aeaf8639f78b57f09802\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x716f5ecb51aa679ebdc99d16e99850c995655270\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4f071a959c8a44edd4e12b7eb6398fbf9e5edad1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc2cdf7b09d3ac7853fafcb9b854b7a760e25a303\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3a516f79df69376e7f2b9abc675e7eb246d547ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf408be6815aa7d7ba98008214432ecf229390170\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x89c35d911d3ed110d37005999e6d8be68b30cd9f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x11d7cd7abbda4c1cedcae3def37458979fa8dc0a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x60b4f46dfab7bcd1b6bea33a446ba392d6a9f321\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2f7016cd226417c2d6b2430de40cd9e15f0d119d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x09ba1ea23e1c965d6d44bc2ad7be72cb7960459b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xae5825862ebcf9d1aa24926b72d8b5fe6b9f072d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1bf3dc06490f034383b3bf5181552b36e1bc4aa1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc8bfd3f4675066e0ec4231791ca4987dc9a5039f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfc3f5bfc9f334a7a0fc9d01c080506df206f77f1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x30cac314da1b401340cf5a7d2227c3c8d188cbf1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xeaf3c95d8fc013c4a5ea6678233390625291c20e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf347d4ac6a967abfae283d21f5bb41375e15ff5c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x372372778da16d37f7290a4bdfb9379375969e7e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x11f542ef4a3a27d2f76c0ba1a69f9c5d305067ac\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe04b86200cb31af05f3e741241c0a850f68978f9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xec19d4ed074090e2759b39f7f809b3a0a9832cfc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf18ba00325e3d17dcbaed349c2c91e1d4aa15646\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc9a53b7369eed499dc1e98a80013e22674193c5b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf935a0d068ce24de132f1509db11e55223fb1abe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x490c4c39f1a0ab55084805274f2c128740aff9d5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc3913a084b4dbacf7a1e55d81c202c829a820c4d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1fa8cec228dca5a4243a4d940f3d1e95d3d65618\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa49e1b80c2be870c8d74c996efa58f03f559b948\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfb99b878ed4456c3a6eec12135dd21613060effc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x44178c3d9858e86979debf4f122d4d1d910cb792\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3a88ce653a41402600718955aeb1df71af573e8d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1d11930fa2635356257661877389bfc2dd022284\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb874753324674c83e88889783a2880159c3fdbb0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf06eeaac63f37a93130dc700ff983ffb04503fe9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x20948e33258654d81df4bcabdfed385e82a02522\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe39794ceee24f58a186c145b43404608a678a4fd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6e8907243a546da5b70752848cb194e319822d44\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd90344ac3437e8e2d255cd506b6dd1c54af2aec3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe393502f1bb5baa6a46641f1b7a76433a6a0624b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb4b1c841e854b2817381c4bd990e5c49b3a785f3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7cce9c769426d3315b07e72071a61d596b7be06e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6a3b2720fac06e966816f9cbfdb0e111ba372d17\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x830e5d225686e4600138fa5ae3b362a0d8a4baa1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0117fdd46130b2ba02cf837c1e8f3aba2ef8a98d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x45510c134fcba994965b1e7381be8d91da10b239\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf66b44cf1b0c1fbf69adf5fb4cb519aa9d30f173\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfd64b1a3917d326c5184fc91ff3a49ddd1255985\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc1b0efc5f03844d02d0dc8f81cbd1ed3493882c8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9d2b9e128a42dfcf5c8a54d5d6140ea5ba96e5ed\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8ae6f6829f89f9e6a64ae5d4d31db2cac8f0b0aa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0ee7d032d120e7bb28581fa345cfb03c38160a08\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcf4c6d7df747a0947a04b1033133cf8c569e8270\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x570245d5d27a141c1d3a11c62c60f04bde28aac1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8e8b612e2488e572d9af0c5c04a9e5e841fcb408\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7495a7476ef5b15d4b07459ee90783ec6c0aef74\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb7f4cd5475311be3f130cdf9a93a89e21f3fe09c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x56f433fce086875322cdcea2be877494f9e0ff87\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6da209b31763b161fe5cfc2831b50ac3be256008\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd78bf520b46d8b8eb0351f9b97487a770fd63089\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7b72457349678bcec2941457f9fd6d825649c517\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3d736583fbb72bca80f863fc1caf043cb2981026\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa2f4148f94d0baf33b1ade5529f31c75ab50c3ed\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x40a858f233b5408d0803003ddab77f1549e2aef0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb509a9958d452d95f8c1e7ae386efaeb4bd13f8d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x85c18317b8d1026d3f0cc3a950226deebabbd897\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x378a820aea05ad5fd16818192f04b556a21e2cc9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xee9e6630f800369a1eef08e4b460a6d5ea586266\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x54269ca47236c0b5c4ef413248ce797f80c02b39\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd8af3ed219a16cff15b874a98e8c45c5b730dc10\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x27433abbde014a29fd7cf0a09c6ddc0e68e7f1b4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c9590ab84f9a4b61a15cb220126ec31995ac738\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x39f763b14996481909ab447983fa361338d049b7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa2b7bf6e168781e1df22311dc49b929bc3e5852a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe8384583539075458c09ea4c906b64e33023ab80\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe9cfa3ce223fe4d9de0219f4f8c3b4ebb029c074\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x850b5a9b511b124137301293d48f1d62001edcaa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x58e174191e06786dec77b5cc2f4e2506197d3789\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x914a0d45c30ffbf7bd4ba852e3ecdfc83723d7dc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x22d10d7a637fe4cdaa0856293202e1d12f7bb43e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x08c01b9822d900cef92e510c3f5f2db63d102b4e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x13acfb6025be068d00009cbb0efddf3cd8dc2fc9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9f376358861e1ed815cf2db61ea047143ad27f6b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1f4ab9148bfec95df02025df27cc270202a25663\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xed273259634f89db660272e860e182666598516d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x010c95e7a7c3731406f4e225c75d4cd313183713\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x43fd94dca92c4f13bece17a82b915a28400a78a4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2f9c174c53c5e01340f76b5d9b78efc269a4cf35\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb4941a74f2ae10a39258272d3edebfceb8b3f449\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x885f84fe563b0828502191382c86642b92789dfb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcd83d0b7f35052ee2d7ffdb31b5c7a6d3e926f8c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x809c87913ea65fd5aa15cce10069b9f9f5dc3565\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0e78f9caed0a31de53cedaa5b99b7614f01ae893\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6133a9246bc1383b26bbe45ff0e2520a3bf02751\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfc5bcceb87ba6c9f3bd8711bf5c14fa7ef523fd7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdc80fe1fe917448178ef30411bcf8c638c197994\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe9648a4e4dd0b7d15e8f0cf6529177a8eb8853b5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x06cdb994802d95298bd2582f96c65e409036f1ba\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9f8104221e4406216522ed423ddecbb419441e65\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7bde4995c371b3988388e51c9c65d69e22278dc7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x80458480c1dc7f2d20607fbe82f6c36acd012f71\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x42581d4393f6244d9859df14d7f7e8e96ea3542b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc3e5f5ce5776d86b85ea756133d57aafdb809255\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xaaa28cabadd672316c73443629202b6006f1ca76\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x477f80c62dcf70cbaff887af387a5e510683bcd6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa875e6c541f092b585b9f68f5d11e6edae102f7a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x31f3170d541a339a9e0b304f519f1318c78654e0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3f4b0cb789a7bcbd566829fb764c155c078d8bb8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe62c6a15347650650729ef03372f9d5bcde86061\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x75597d2eef2e7bde52305eba44e5a8ec196acc8b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x61ec7d8be9c2c3d3ad30ba5cc5d676df30ce07fc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8032c883d23214aecde234aa902faf9306dc63a2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x26ad76c3ebfa66bfe3f2b3b8c37f47684bda63b7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb1511427e08d06be611970ae803c1087f8fd6335\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0b67421525abffbe4a15c0e6478c653aa56acbc3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe5330dbe8ffd44a5cf0579466ef8882869471d60\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4273bae93cf9940ef56a56ce2606910ccd35ab38\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc3c477cbe8563942538134c186703b9a5f7d9baa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa9cc61f5e6e992ac8ebd67f0d3ce08d4724bbb65\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8c17934c279c258feadcc7e27a448eddd03cba8a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2084ff44da7c3142dfd6e79bd2dd0846975c45c7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4fbc4f91ef5a13f0ac78a6815ec8c17b8db748cd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1d8cf4f474b33619594143284b2ff42866011e13\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x660e9a1470803baffbb2beacf13d70ebaae8d9b9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfa19aa516c1df06a081b43db5e2bd4d33b05df77\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xae2b394ca5f49c0eb453fac6544374a7e44eaf7f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x18c38799a23d994713624abf25613d8f5b96478c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x430f16c7dc938e731adab7b32127afd8300cff6e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7a552cc3911f630d4566b899a58f053d1bca14e4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7b4e919548e7cf48532927c057b844c70d87366a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x48de8c40ab749ef4af1aa96676dc408bf03c0f2b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1993ae40035ff12e06dd5641799f9eb8498a968b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x06bc47eb1e827b142c4edce424f41c641b6068af\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0deaeee016672931231037612212209cad4e8a70\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdacb2fb431edc402b510fd9b13b702dedbd8c536\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x86174eafc32d25df83bcab01f465b9eabec96aad\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc47b8a6b8b3cd97db5880571e86de852ebafa12e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2efb1f3471b14822f1da94c508f13c3ec71af97c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x87b86bffab44e27b4a2239af7fd5b7a6b1ed90d6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf28671c14f71c452676b03261edace11325e5798\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdd47107ca66158aa3ff1d7d494feba1cdaa8f93d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0cda464e19bc0e59be183d21fe1df7cbc367dc8f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0b093df5cdc9ba398dcb3be4530eedd65b27a2a9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe37a61b23d5056ad005c719f08ea98eeaa53d885\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x72b852bd99b7e473279f39f2c8db41da574b1e32\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6b31bf6cfcd672951f0af5a024d1ab90d73625ea\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb483af466ba7a5861d986b6cbe7a8452252ac29b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x627895457ccdd90ac8d7c811e7e78a83cf68e4f2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x65293a45f865666cc0e6d0a59f52e315ba63b308\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x434d19541db0731d22d6376b868fd8f072d1ec0f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2a795fe8fdd3e5d8044709b45d8afb78632068dd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4e714432916212d2940adee42966a8093e858c3b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf483159d88130a81b796dbf4ec65bea5cf5b0823\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1f79994e78f8330f24b6183ab263263f9f30c278\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc23b992c278a573253cdfcc6554ef19bcbd478d1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x53a6c629374c2592ab1604f266b5aeec31bdecd5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x750f2547b79f2d469300e3f7ca8e90ec3ea347f4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3ed58cdbfe8629ab4cf1a34839ba9b48fccb047b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x65eb5235be582fe1b261c45961fa41980f4d1165\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc24e3760a94fa13a3817e36ab78c6ba5d699cf3b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd988bef29a246d60d763efe4b1e27bf117bd98c4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8cd72e845249f49e291d10f897ea543806836db6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x79cef81410d0611d2beb94ed06c5c163ade67e8e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa79e417ff1a926556cc351c3f1ea3c7b33e9cf87\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbe59261fe356f4f18a1ef26855754acfbad3cf24\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfcb49ddb9330abc55bf8184bc1127757e981782d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xccdf6ec7f36a01168a72a34c74f6aab16e9b2cdd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x69cc01204949a92a69886cbe78b5546d58e21746\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xae033550df7adeae221a3f9f444aa3810e34c6a6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc424d0a9b92eb4f08da191760b69946a6ca10085\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa418466864e054c4c26d9ea948420fb5ba0fc990\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x88889f08c7187880cd6bf06fafd72c7974d72908\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x01e84542f70208255526374b008c0dc6f982d461\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcd86cfcf0e8021bfbd8ce452a6d9814b29383265\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x89715612091c41c3dfbd7e418f4fb5db3e732348\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f973720df3d628fe6c4f97a18e078e0c4a478c2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfa79b99230fecc72f57624f42f4783b07d7ab4a5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb6882de9b6b0640be1052ca8bed5798505b893f3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x82114de9e819164204175b10d5e8ebd169327d3b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2f2b30c3cba31a94b130b2d18729ff39ced22810\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x86cadbee39576dda2c2f52458ad7cea997b2298a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4c926459e11874c1416a7d1d008543f5fd1f475b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3679303ffb359c89fcc1a580f643b7d649463e81\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0e85a905b870d836ae43c4978ce5b14e0001362a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa27a134e55197c6ec7c46cb5ec0c44a0cb1af548\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x46c9a3ab004fb5cb710c579460e0a11c77aa0e74\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xecbc4011d149a86407ea76e71bb1850ee705a14f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x97f84e2965e3eefc2a784144294693872d3e1454\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe0f4aaa3dd3249e5dbef56c0bc2f63b5b12a0c1c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0a087301639af48274af98348dd1c9bb7d6cf901\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x89efa8cb1b959fa332f1f10ce94d2aedd3b15958\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x42cfa37c308f6434c276f3bdf118dba23123f45f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x561d7081bbbc1ba0045cecef503c19fb82ebe074\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x57269331a3ca4ceb5b08a9c897e01fc2075fd62e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc2a42ecca74e0acdff66ba23dc2289c5395029f7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x64585aaa58ccf4c43a3a8973253d035f42f6f770\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe5ce5a19c862e154d404dda97e7c1e6d20fe37a0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3c9df888499c7a5ebaf456b589bc39f752337c09\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x668e67199e5005f9e22f830da7d15c27036e6bdc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd0607835ebc3c6329b3b8f6228662e57bdadcda3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8abb18b5bebedbf94f0f942ce4042f33a6c23333\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf56b0a52c53e63333b97b2e370ed4523ca2b48a4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x137e076d1a3c2f2a4d026bc7a3585ca6cf82a645\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe6bcbd13f214e7222b97b407a1bd58fad9972a7e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7741cca7b222801de2e02e5e1e8c3303337d14f0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9b697bb3ba5b7aa6b0b6644c2d1dcf470cbf2931\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0cb02dfe351e030435baf14359ccdffe0e78af9d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5a5e9c7f03918d4725a09d6498fff3c7263642fa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa4ecf59018b19f5673f96501f5c3b874f9e3844b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x628c24e4412721cf8ae2b81de7e540709ba61519\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe6808bd1e6d2f15eef15cde4e8a88f2b150b06d2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd873add826b261d8cc215d16e521d7e196eb3765\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd92e7e084b3887f6a3cebe22e69922c2bf804bd5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdedafd5b8108736a1f271228fba38492fcfd51ee\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf4addadc96a5e5cb98ef95ef9d995ea5cb3b3749\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2e195f59f1cd6d3eecf97351947abe66bf246198\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7408926b9bff8ebdd17cc0a174f3de0336e6323f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x785c7935621ceeb727c7caa046ca517fb19aea85\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x049348803a6d9593f0ba679d9f05193510ed9338\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb0167e91d9327a43b2db28b1a675380c05ef139e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5441c24a509f7043b5f0bff8515c10842739447a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa57ea89602204d3c7ce92b8070165e9879316bfc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f6c598b68f4917ec4f54f03aba489fbd65be9ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6bc0881f2bbae93f0b4c2e2cbe6c8a342ffc0e0f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xea39f792191b0780f88caabdfd0f539803850944\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x49eb8558508feeed913c9de7168f616ad5925b32\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xce06b2dc5b2ea30df5a7a2252979a26a2123153e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x070ac35cca5989865fb6be7083a5907f9d636eca\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfef1dee249829fbf4a7f78be610f8c73eb04855f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0ce93e3201c3ac455e3a934cb049f90ed481697b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9008c39bfd01596357008a5e7f34e595b066c139\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x10c08b856615136c103f1b5e24e2b3136809df94\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdec49f825bf611b8271cd4f419969230de6614ed\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfcca3ade76c5a04cf70bd0a3f4fff979eac141ca\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa3cd797ab21f7cb670d37363bbd4a74f2a2dcbbb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcb81156d606389e8fb1db80a93209b9e2c575e72\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0dc75b41f57fe00e47cfba69ba61cf3c271863cb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc2d9dfa376d197c1c6fce02adb9acee8fd85803e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x40f0f512fb0a2f090246b01f6e9edd2b2558422e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x364c95f8f2c7a1656165e939533db3278635af76\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0e0e937329f464fbfae556aa277fe2f4afd9a45e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x484994f1b8604f600c4f3ec1e65d5afa3576acc4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xed8752b8a78111ea55fcbe48bb58add6cab6a7be\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfab935df5cbb0c4bf485c2ccb557503fea07a949\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe10be9dcddb5588ac83857b489ac8775292a196a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x597e8e4835816a621e0e635cce6304cd9b6d1b45\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3366663d826c830e108361439606ab82af3b9dc8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1a93aa4e4d00700a56af1265cc080627cde56522\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1133e938080622e323ca9522040fafcdcb40b926\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xee079715edcd6bc5ffaf8949dc1ddc468ae1f82b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xda3a9fd4bf83df35e588b7ae2fadb42e0dbdc70f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8ff0377764ac3663e72314f0bf2ef96eaa97e282\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x34bd64794e5411ba62854caec04f12aa7459f975\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd7b8580c16ffa01230ce7335e0eeafec9118354f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe19d5f3e3df4dffe88a420ca7355177677c18066\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc828e09c3272ed2ab2f2e179c4eed331908e6384\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3c0c651f74dd2162ded4ab611280dd3969b60a84\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x057b9a5bd82a3bbb9d51fd45c919ea585bebe67a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8abdb10a1ea697e0471ac1d7fff88c7ac92bd5d2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x04bf364f32f5aaf4144935dfbdaf39489e4c291e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x907482f779c352ee590ddd446e26d148251ef417\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe75aaaa24edb53b6e7facdeda96c9cdd3af93c53\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc0405f4a4a10a8d624bb94e193d5445caca28e81\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xba42574bacb0487343c7d3c1765ac798c1002c76\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x254de1b17ad22fe34c7c36fb12eb601bd39e0fd1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0472ceb9f340fad1b15e2b06ecdd34b6f989df95\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbb884f173ef7e55363adcf93da3a8ec4df1376b1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xda493ae071ce294a8335e918a4720e757e7d1817\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0f2a7dee298be712dd35f5607d87cdca6dc8aa20\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe8e02a97a77a5d95e1275a31c21f6f9d8bcd7910\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0a1a546b21a090ead8785a88f50878e671900780\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x615fb4df1c6edc0bd900cb3025fd8bb318c47e77\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x00d0dc642d2a5f053807a73f8ebcaab2ee57daf9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x22a4e556ea6683111c8d90966924b36b89530aae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd02cdec33bdf2cfaa4b0fa84b7be2ae3f37a100e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x513ae86cc6f17457c67836c59d0db9431bae292a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3ffe89cfb94e2c640ab665aa43ff7af00b79e43c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3cfc4040f0556d7b89cf54ce39702ae82e91272b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xca0a654bd79995c1268ae6c3d431008df987ace9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x96c8945eb534f729a3326d13a02d2c6258595486\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x14e9190974b749cf22dd78abe932c1f354224df9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3b8885163203732a78c291466f25eadbda8ce635\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1ee4cd4a47d1b656c5ba3a0ca9ab7d34042bb759\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6ee778c7779a6ef0d22a710fb36838ffe76e6bf1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7fcf4425b2510a63e30e0f47d387242702181641\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8156566f8730d3a379f1703408767e317cf679de\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f08153037ee56973029f711710d65956fec1ea2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6502ae96e8b2ed310bacd897f3bae0d7bed93dd1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xafd320d0bf1bd7f3a6a7b5b0930d7a619f8957f8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2f75c8485d3e885ca3e47b93d31196ae022e2cf5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6ac000b4edbf4559d55a64979a192d05bf113e14\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x612a0b178a40b7d628463b928ae6fc904adaa882\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x31abdd2b134c92200efc731c2ee7893cc8871a7f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfdfc0e8faea7c3d0bdc5bf6ec6e1c681ef4e8242\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5f4b557e487007f6f1145fce9e4a42665dfe7f38\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa8d59c349bfc154a81e67ac174603a7458d5f157\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7dbbc365a8f739a18762815f34ed29a99e64f5f2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2ab3f465ca3d3112eb584b65b863fa0d6e27ad82\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x623aedc09abb2900624545e07e6469585510ce8b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xaf4f4cc95f9dfff1e2e049f32e3385dc42c5a9a4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb1feb102fb9eea85349e1ff4f6a02f2d8fce862d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb25c4729e1f2ca6e90a225bc9d3c228b64349e0d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8d3ee1a8137642ac7f2c6930c4239554f22097d6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x61ef0953395cb847be80ce69e78f1ed58ec0c806\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7aad5942a4b741912432a24ce5202e3ebbb63f4f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5e95ae43d0024a851c0e07f6308fec3006091770\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbaf1ddc5cf4ee09cf9c3f70f62f22562d375d7d0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x83569f2ed7ebd48a638f70c256d35bd1838b8a7c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0076a1d0088ee73ef4e5f7d0783530ddfff90b4a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xac3e26b3dbb751101bdd6b16d09aa2aed0265030\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xae5ec73ae62d3f3e1f9020149b89f8f0bf9d5e05\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6c4a8d8a2d6da7857024500903c9fb90f6989a68\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x07958969ceb4f51e731f6be1a9f2e1d28b13435d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9b060289e677d87dacb6933dc9d67624644a03e4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2648c940db02627f9ddcceafe3878c3b838b0362\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x241efa98f8110b1ac88a3b918668ad60203eaf1b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x95b373eb12ce4840f86d591e2a25a7c5136c0f22\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xda6254963e5c0db5eed3fef227021593d738e4c7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe7be70653095d17e7e0f0db5b36c25dfa7378ff7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6ea872a70f99e7dd0d802de0d74deb32a42df28f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0a219a7ce6ea411f373d59431965a23cd4c65a42\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc3c8c273926a39f612a2e93c8a9c57f46c2f60af\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x82047e445d6ff4df1d780294bfeb0bd64f2b29ce\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x23f0371be5a57e1625997ed60050a80b1413af2c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdcf80f6c601340056cfc11ba051c0fe69fedc65d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3d261601c0433381870d16aae2bfd0f890411bc1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4402ee5ea0aed550810e61ac59c50b05f5ba9a52\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf1f8cbbb5bf20852d087737295d9487b2c8f9fb1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5b978c61d3ebba76b519bf8d0712d18e5f86d2c6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xee539a28275e9d63c2ad612d2048d6f10abeea82\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe5ab8fc1720cdc84980785c4ac0bfdefc263d86b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9f05d585284baa3cecff0e47446fa9c1750fc4b7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc5a3c42e4779f2e6eff32eb548d7c6fc1aa22393\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1febf5d1824b9159f815b6073e69f9877b494e46\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc5162d0f7c49cab9e4329fc081bc96cd3f4151c1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x28ec4b9bb554ebac2602954e1fdcca4a9d67c6d9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x46ccd72c8e4e1fce3398edb9a25b66756b72e889\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb142609f372d57c2dc5e0fede218dff4be6b0877\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x830f9c51662f52ab1def7067779fcdcdc491335a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x88f8c0dcdeb68f435bebfbdaf984a1031ce4a355\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x03237296a99bed8bd3b860a6e5362b2779b2d00d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x46e1ed298c22680b7e1821125d5805dddc1a5972\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6b05aa0e8e72811f7778eb44af7ad02d913771ed\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8e8473474b75e36891d506c4df928036a68c8666\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x11e28ebaf5f0254ef84712073dae0137d229560b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfba308d7c61385e0f3ee0fd38e310dac48a18b65\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf4e021377420afe90c1a7d2b8968904946633a64\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa27d897e9947475140d6441f6b281db9e1fbf278\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfab12a48f0ca675530be5ba7f6a67cf6f91d60be\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2a6597c7bd868e68cc0edf321735c58851785a40\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb3f9c7fda446a6d974707eba889c72cfe795f167\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3b2d2c549014c975405bf2f37d016edfab853257\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xaf246c52bf09cc1652337ccd9e3a9f0f0e6d7c8a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8edd91ac504f578eb76ec50b816d7a81f2a2b996\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe9c47f38e9dafbffeddae8393bf225b3f02164d2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xae9088735fd4767b08fb2a426ffbf885b45e7a6f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe4ab204227c3442ff2e5489f5c22fdf60cc7e20f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb64315f63c434f3954fdef0bd834c9e61ace4e11\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xeec572e47016f76530f0ae6432a8c2ffd12aeca3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x60714c5d1d75a0c6d2c8c75abb6d2a2cb76c4f46\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x00c2b1388a83f05dd6ce89e4d277e541468bf4e3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x01121cea027c43d90b2cf7dbdb7e8e26e717e753\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb89ac1264a304c0bf6c9c2422973000cd45de21a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x85784db4adedc910269b22081991393912c85692\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1767a912d4622558e120c98582afdfcab0d583a2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1bb6e40563af3fb2bc71128815bc97cd09613983\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe2ad2ffd025fbead5dad5a429a51b7a2061baf6c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x64854df1016b44ace87a4e24f6487ca7766740c3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8af3113986a2d82bc86d2f98eb21858a5f83ce82\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x319bbb7d0dc546e800323606371a46a6366d3787\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdfa2f0fac6f45b021e54360ef59745e800f3d3a6\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x33a2b0d026a220765a8a240cf6ec786acab480f7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3cb27e47f199fe662bee9a8fd1e12c8d5793ef1c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4d32d341a43da2b22f706f12245592869ee5a801\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xac93382128b110264f5fe8cae52694e22c635bd0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6caf4b1c487ded67edc1415b9e3a49ed15e71436\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x633c4e4796cc8832a1a94b382ab41f6cb4f97ed3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x207be6207696f99de529ce10282713d79ca1da52\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x492dc60ac4d2b5aca25905e156d6c9700e8713e5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2bc1eac485fb46462990cab84e8e4ea6b0983c5c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x24b925b98397a7475c841d0b59ce1d74630b944f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa4080272331813407c706d87a8c140e564808e1f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb4befd85837bb472d4ba318de567841cbdf45a48\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa362c304cdf8547021be4c126ac2244938986cf9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe2bc6342263ebd3872bfa56c9614a12aa3f0a7a0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbecf7d6ff3071bc53a1750541376211af2b425c8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x16e7802149775754bf737280f657576ee044ddfc\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7463a676e3320fe8ddfe53ffb45e1e76741b14fa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf70266a4a70609e886572a9b75cefdf05ee7622f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4783c9ff91875c5958f99d85120c3d7db7e45494\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9a894efc3b46e28fe6f62b38c75324ce0e81fccb\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf62e26ee7940322a68591f1b8f999b3de84c2490\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb3fcfa6387b43e402082e17fc8a8652aa27ca143\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0e1b990ba0d3910514640fdd4bd0f902e2092c5f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x03ab12fc8cee66c4e2baaa6aff85673da037b156\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa69a01cc98229390030c0a239b17464b9ac92548\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdddd49faca5fa2737eacd409074670d746c098ee\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x34f3add7daaeb696c578c28f3d330782da7d1afe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6340dad3fed0186c6d959c4974e5954939057546\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x531ed6b1e385d199f92f96c43ce4e443c7cd8a56\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5bb7a165170bb207f00d97f595bb5c5b539bfbbd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x71405914f8ebc453014bb533966f0f0c0ff1aad4\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xba13c9f1a1c973e6ab6ebe0fcfc687de06be88f8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x290a761e12db29ff168f76598311d60348be4c73\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7fcd8448c87b3849ac37ccd1f7acbe2b8f97c273\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2cf69da1c505041ea5f50050504a67608cea3e57\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6229a6f9bed1b0683baeb4dfd8848cf1cf0ce44e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x753f14ae7220021bde75a084b76f420294509245\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8c894824fc26d042867b1a258cde001df819a47c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3906f7ba8c19d95adb22294a775e4b994390aeac\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x595ee42544da97c531debddd6ecd15247b178e82\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x14d6e32d3a4aa970d2379f4c3396415727397463\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x901f257e6fbccdd111d505fc759d3bd4084f9e53\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6639eb5a73fbdfd5c385c2cedcb908e330a1900b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7c9a0fccaa3e0730181d6d20af4af2fe597ac3b3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x45c32aadf48486e8b90c2c1b94c27a937305e56c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfcda509bb5977e04cc68efea5d8e0dea5a29adae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xffb1685dc6e85981996d5d4e6dae08da3fc08cff\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa7562c241680ab7c01f783f2b13da4f18880c72b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2c424bb892c724539f4eeccedcac075d314529bf\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe009d9f8ead34b61c7e18949944524f563b414d5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6b43ec5633488d44f655b676f4fab711770bf082\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x348455de3028e266a560fd4e14190563aacf7c0b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x731eafe9f3f1c72fda5f93aeda51a2d9a3ed0b6e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf10a8cf9bdce3bffc4c2beea6ca48767098c2f8d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x07fe8d42864f3695032b5ef7ed95f468df5cfc00\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1ab044df729a177d81b1b8dcefd4fec92df99f14\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x835f485eac0aa97940bbff5c746c718d451109c5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe4664aa2c384e103ed3aa30902fd1f53ab79f21e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdeb6a8eec36d9301eff3fc06f5143ef507aaa4e3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5faedd84418df7ba461f5e3edb4234fcaeab062a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8f857e8b38ad8682d134009e1daf3f1816905ee3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x96b14ec8e473be8987497ce686f2af085e8718d3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4cae514ffab8ca5992fef92df3a6ed470bab4e8f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x88ac10c092fd728b17f4f63f844c8059096087b9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x30bec77539e68dd2432c66f55566bc54d90ef2a8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x426aa98d552e1afcc3f78c2b0d53e82e1d557041\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf3350833f4029a2500d7d022a39174bb05c75220\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4890c9afaed68b78af0c5a4156c15e8428f0e926\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4501d21b533f236f9048a24c5943b693d728fbc1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9bae5599f30e5bb0f14b49cd19f422433a3f1476\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf165e51d8da2431b0e22e53d21b6a4ab6c1abaa9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x68b172ea70d03caa42f16eb12387494895d56572\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x2d2ae65b7882ca29815e3bc36517db08ce7ffccd\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x21bd0167c66f1292780bb46791f3b2a61533fead\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x424c41081727c0d0b3f5154ca93cbfbb93b7e24e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x64a834073be7e7b3be00ad267a46fcb7805baa15\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc9b72ad2084d56f97a314462eb4e46bcf08e929e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5bcaadefa9f4218edb73ace31035835759ea4d41\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x35729dd7c295818caedbc119c85529c91c788e6c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4da27ed18c78d83c9d1908898c13ca36a9cd80f1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xdb6e611b56dcd7e4164366dee4d8c20bef6ca38d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9ef7e51dbe375181a8a966837de4d3980239c91c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa38c9b5104ec030561595f90024051eaaa5b2f2b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf31a106e793e10dca6a64c2145a9921017aaab2a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x1cbee73952d62ddbfed557548b024c1de641221b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x081ea257a31b413c9aaf1de9dfe6a3ea902ceff7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf6c3bd74c137566a1220940e77962640eb05b602\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x41469a3451cf514858eefa40ea045e841adfa563\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc9c2a6fd96966f2e2ec4b5e6185164511b79296d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3fff997a1ce27c241cdc83e5f4bd1d525b061987\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0bb5c025631fdde4d6d6b8ba485ae3a3d4d3a3d2\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc2335e6bf4f2dc4c8986ddb5ddcaf096b665f029\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd2f9cc847b858cce8dff299e73dbb2c00388d91a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xeb98e5682c5fdbb151827b38218096469ed4d4e5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x094314e2d69197701e3b65692b9527bccd3813c8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbe06ff8673a6fda5750fffa082598a4f1f23fd17\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x724f716c7ef32033ea017b9b6af4e287db523686\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8adad2a23807838a709ebb71a0d0f7b57f97d7c3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb0a119492e086e7290a3ecff6f5546659b2c9c40\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x481ee22baa3fb86643b8148e5dc293ba38a16d39\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x9fc34899387eddce47214cf2cefb61f95c551a00\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x03958e1129ea46ae9c2d799e809874465cb77bc3\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7ccf738a4a051e9fa10c85e6fcea2e5a11a6ad88\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x083c768ac444151a12eb92683026defaebf0916b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x39436d09a8ba455ef4b99388a79071960403855c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf80e3adac67a8ad78c4176f9de9f23ee268a0b8d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x7f1d27a16d434366f3b0797e5afd286f21d1643b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbcb058f1a542473208f47cd59d4320ec797a6670\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe51605047a50fc70143d98cb0b090bb1b157b6ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe5c53fb8544778bb17ba4522723166cee54af872\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xcde1959e46d18efeb0afa7bfd7c8835727683780\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4bbc6cecd418beaed98da3736fe6612188131ed7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0cc292621af3f604eb8e5ce504e302536713d23d\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x48fa2fda14f2c7cdebd97fcf2d32d72462d9de83\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x118abb7d96b2a5e9c11c0eefeb518152810588f5\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x718d43607325d146d966890456e09f6657c167ef\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x896903f9b1f98fcdaecbf6632ebc44455a24385c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8729d32e5e896890e0fbf5e6f168afcf01e487f0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa08f4e24fb72fb095048906bd664441d1644042f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xba310a4a43cbeda67cf55e0f91c4449739c50442\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x08b522c1bbc0e3e5094eb853af8432a958eda970\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x90e87e067435eb3884d5eca757afe7df7f4cb609\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xa6a8d5b1611595188b931d1cd8688ea4150c08f9\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc3feeb734cee7267326d807c777ff750e882bded\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x40d3c73818a699a9cc726eb8efbd3d023f073514\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x18ebaeeaf41ce1c71adc3a3e3f0bdba5e494a3e1\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x72f52078ca687262aaf2b4ae52bff69bc8c391fe\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xb3b2ecaf38c51e6fa6c7264e2073272ed673b0ae\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8c58ee9218bcc66f392505aa2a37d625affca39e\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xe5c97f51b2cc1e91cf9df5db795b7d81e408aa7c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x06ed033d777ecc6e9dc0ca2082cb08a6ed1e9295\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4bd0a47162d36df4b2e9d3341205edcd8c02cf56\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xf83c55f2fbf7dad3d0d3c702d15173a4a8a77ff0\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xbff7e233cdf4906903c62cde36936b4bbc3c790a\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8d7fbd58cd17de6baa9b65cab2abd3b749bf2372\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x260e7ca8b6eb5cf033cc7522151085d554712d6b\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x864f6d1379d39632c89cd51ca25223cbcc6a7c3f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x86f3765d32e5110b5a49585be724c571a07a2767\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x17a253aaf300d4a20970675df3ccabe938716746\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc52d2e106635b9e1b5c05d641532d8b22587a30c\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x07e17cdbb0ae57e89583076b467c0de20934e1f8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x784892acd0d6b858ff405372c25e9629fc304e1f\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0460866e91f11cf5c906d6b2812e7d2c626618fa\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd5f793372e9dfeb7cecef1c35ad9595fbfa12e88\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x0b63d67989fa94e702bd976fc33d83308b7ca1b7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xc655cd1e5aa4b2c64297250a7d3be1389f1bf075\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x5763245ebf105c3e85c1872fa7a3f737b03c8aab\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x89111cde256e71faf3e0b8849642041ae6dd17de\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x4234b2afa2788755793f022d1f74559d20ae5143\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x82f319d1b0eaa2f245e91596fb37d6ec51e65914\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xd4ccd6dfb1f844faca190282b90df126bf6d6ad8\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x89e11e5ccbd2c3d93abbf335c3ce5f7f76e7ef15\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x67734c2a9805e0beb8f2144bac61b6a8a1dc7443\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x3ec71a4c026a315d7d2415e901a1cc923aee1474\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0xfffEc6C83c8BF5c3F4AE0cCF8c45CE20E4560BD7\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"},\"0x6fAF1803Fe7863269C8433f06d4A8AA20E88C43B\":{\"balance\":\"0x314dc6448d9338c15B0a00000000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}", "message": "localFlare" -} \ No newline at end of file +} diff --git a/avalanchego/genesis/genesis_mainnet.go b/avalanchego/genesis/genesis_mainnet.go index 50a8aa1b..236d68c5 100644 --- a/avalanchego/genesis/genesis_mainnet.go +++ b/avalanchego/genesis/genesis_mainnet.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis @@ -19,10 +19,15 @@ var ( // MainnetParams are the params used for mainnet MainnetParams = Params{ TxFeeConfig: TxFeeConfig{ - TxFee: units.MilliAvax, - CreateAssetTxFee: 10 * units.MilliAvax, - CreateSubnetTxFee: 1 * units.Avax, - CreateBlockchainTxFee: 1 * units.Avax, + TxFee: units.MilliAvax, + CreateAssetTxFee: 10 * units.MilliAvax, + CreateSubnetTxFee: 1 * units.Avax, + TransformSubnetTxFee: 10 * units.Avax, + CreateBlockchainTxFee: 1 * units.Avax, + AddPrimaryNetworkValidatorFee: 0, + AddPrimaryNetworkDelegatorFee: 0, + AddSubnetValidatorFee: units.MilliAvax, + AddSubnetDelegatorFee: units.MilliAvax, }, StakingConfig: StakingConfig{ UptimeRequirement: .8, // 80% diff --git a/avalanchego/genesis/genesis_test.go b/avalanchego/genesis/genesis_test.go index 7dc8b6d6..ebfb0439 100644 --- a/avalanchego/genesis/genesis_test.go +++ b/avalanchego/genesis/genesis_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis @@ -153,7 +153,7 @@ func TestGenesisFromFile(t *testing.T) { err string expected string }{ - "mainnet": { + "flare": { networkID: constants.FlareID, customConfig: customGenesisConfigJSON, err: "cannot override genesis config for standard network flare (14)", @@ -239,7 +239,7 @@ func TestGenesisFromFlag(t *testing.T) { err string expected string }{ - "mainnet": { + "flare": { networkID: constants.FlareID, err: "cannot override genesis config for standard network flare (14)", }, diff --git a/avalanchego/genesis/params.go b/avalanchego/genesis/params.go index fc80e003..dcc49ee5 100644 --- a/avalanchego/genesis/params.go +++ b/avalanchego/genesis/params.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis @@ -40,8 +40,18 @@ type TxFeeConfig struct { CreateAssetTxFee uint64 `json:"createAssetTxFee"` // Transaction fee for create subnet transactions CreateSubnetTxFee uint64 `json:"createSubnetTxFee"` + // Transaction fee for transform subnet transactions + TransformSubnetTxFee uint64 `json:"transformSubnetTxFee"` // Transaction fee for create blockchain transactions CreateBlockchainTxFee uint64 `json:"createBlockchainTxFee"` + // Transaction fee for adding a primary network validator + AddPrimaryNetworkValidatorFee uint64 `json:"addPrimaryNetworkValidatorFee"` + // Transaction fee for adding a primary network delegator + AddPrimaryNetworkDelegatorFee uint64 `json:"addPrimaryNetworkDelegatorFee"` + // Transaction fee for adding a subnet validator + AddSubnetValidatorFee uint64 `json:"addSubnetValidatorFee"` + // Transaction fee for adding a subnet delegator + AddSubnetDelegatorFee uint64 `json:"addSubnetDelegatorFee"` } type Params struct { diff --git a/avalanchego/genesis/unparsed_config.go b/avalanchego/genesis/unparsed_config.go index 57bbd5b5..91241163 100644 --- a/avalanchego/genesis/unparsed_config.go +++ b/avalanchego/genesis/unparsed_config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis diff --git a/avalanchego/go.mod b/avalanchego/go.mod index 3a36b698..f9b4a26e 100644 --- a/avalanchego/go.mod +++ b/avalanchego/go.mod @@ -6,13 +6,13 @@ module github.com/ava-labs/avalanchego // Dockerfile // README.md // go.mod (here, only major.minor can be specified) -go 1.18 +go 1.21 require ( github.com/Microsoft/go-winio v0.5.2 github.com/NYTimes/gziphandler v1.1.1 - github.com/ava-labs/avalanche-network-runner-sdk v0.1.0 - github.com/ava-labs/coreth v0.8.16-rc.2 + github.com/ava-labs/avalanche-network-runner-sdk v0.2.0 + github.com/ava-labs/coreth v0.11.0-rc.4 github.com/btcsuite/btcd v0.23.1 github.com/btcsuite/btcd/btcutil v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200627015759-01fd2de07837 @@ -42,6 +42,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.7.2 + github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295 github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a go.uber.org/zap v1.21.0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d @@ -51,7 +52,7 @@ require ( golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac gonum.org/v1/gonum v0.11.0 google.golang.org/genproto v0.0.0-20220712132514-bdd2acd4974d - google.golang.org/grpc v1.50.0-dev + google.golang.org/grpc v1.49.0 google.golang.org/protobuf v1.28.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -72,7 +73,7 @@ require ( github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/decred/dcrd/lru v1.1.1 // indirect - github.com/ethereum/go-ethereum v1.10.21 // indirect + github.com/ethereum/go-ethereum v1.10.25 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect @@ -102,11 +103,9 @@ require ( github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/prometheus/tsdb v0.10.0 // indirect github.com/rjeczalik/notify v0.9.2 // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.5.0 // indirect diff --git a/avalanchego/go.sum b/avalanchego/go.sum index 85f119ee..a6cfd9c6 100644 --- a/avalanchego/go.sum +++ b/avalanchego/go.sum @@ -43,7 +43,6 @@ github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VM github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= @@ -56,10 +55,10 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/ava-labs/avalanche-network-runner-sdk v0.1.0 h1:m35SkmT7OO0cxrnqHCtQ0v8BHsDw8EZmo+yjH0DTqIs= -github.com/ava-labs/avalanche-network-runner-sdk v0.1.0/go.mod h1:1oDcmKg7Ed2QN3uxl6r5FqMsZMSAsGVBtI4ra0ewW/A= -github.com/ava-labs/coreth v0.8.16-rc.2 h1:Yo6J5faqK9sdnp7yzTIIEGodZqPctvy9l4SLRTVTWck= -github.com/ava-labs/coreth v0.8.16-rc.2/go.mod h1:iOB8EcOy/9yY1+/MAUqI2UM5GUIuIbX2MVKK1gCHqHs= +github.com/ava-labs/avalanche-network-runner-sdk v0.2.0 h1:YNvM0oFlb7A825kGe0XwwZuvIXTKF1BsuvxJdRLhIaI= +github.com/ava-labs/avalanche-network-runner-sdk v0.2.0/go.mod h1:bEBRVZnGeRiNdDJAFUj+gA/TPzNDbpY/WzgDAHHwJb8= +github.com/ava-labs/coreth v0.11.0-rc.4 h1:oYZMWZcXYa4dH2hQBIAH/DD0rL2cB3btPGdabpCH5Ug= +github.com/ava-labs/coreth v0.11.0-rc.4/go.mod h1:IhfO9oA8KicFyYZA3nIqjV/TS6xzAqT5ml2QKfNGtGA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -94,7 +93,7 @@ github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -126,7 +125,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/decred/dcrd/lru v1.1.1 h1:kWFDaW0OWx6AD6Ki342c+JPmHbiVdE6rK81pT3fuo/Y= github.com/decred/dcrd/lru v1.1.1/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -134,13 +132,14 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.21 h1:5lqsEx92ZaZzRyOqBEXux4/UR06m296RGzN3ol3teJY= -github.com/ethereum/go-ethereum v1.10.21/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +github.com/ethereum/go-ethereum v1.10.25 h1:5dFrKJDnYf8L6/5o42abCE6a9yJm9cs4EJVRyYMr55s= +github.com/ethereum/go-ethereum v1.10.25/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -152,12 +151,10 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -197,7 +194,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -216,6 +212,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -280,6 +277,7 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -303,9 +301,11 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -342,7 +342,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -395,13 +394,12 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= -github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= @@ -410,7 +408,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= @@ -439,6 +436,8 @@ github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= +github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295 h1:rVKS9JjtqE4/PscoIsP46sRnJhfq8YFbjlk0fUJTRnY= +github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= @@ -493,6 +492,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 h1:rxKZ2gOnYxjfmakvUUqh9Gyb6KXfrj7JWTxORTYqb0E= +golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -810,8 +810,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.50.0-dev h1:cL3RBCQpJ9B+dJmkAVg0OeAogLIuGkH/kWiXKX+RVSI= -google.golang.org/grpc v1.50.0-dev/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/avalanchego/ids/aliases.go b/avalanchego/ids/aliases.go index 31b083a2..483fb4eb 100644 --- a/avalanchego/ids/aliases.go +++ b/avalanchego/ids/aliases.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/aliases_test.go b/avalanchego/ids/aliases_test.go index 6680d08c..e2f09bda 100644 --- a/avalanchego/ids/aliases_test.go +++ b/avalanchego/ids/aliases_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/bag.go b/avalanchego/ids/bag.go index e71e4ac6..778d7e40 100644 --- a/avalanchego/ids/bag.go +++ b/avalanchego/ids/bag.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/bag_benchmark_test.go b/avalanchego/ids/bag_benchmark_test.go index f1a1f500..30e85120 100644 --- a/avalanchego/ids/bag_benchmark_test.go +++ b/avalanchego/ids/bag_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/bag_test.go b/avalanchego/ids/bag_test.go index 8cccd03e..f99a1abc 100644 --- a/avalanchego/ids/bag_test.go +++ b/avalanchego/ids/bag_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/bit_set.go b/avalanchego/ids/bit_set.go index 5abc9e3b..ee13432c 100644 --- a/avalanchego/ids/bit_set.go +++ b/avalanchego/ids/bit_set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids @@ -8,32 +8,32 @@ import ( "math/bits" ) -// BitSet is a set that can contain uints in the range [0, 64). All functions +// BitSet64 is a set that can contain uints in the range [0, 64). All functions // are O(1). The zero value is the empty set. -type BitSet uint64 +type BitSet64 uint64 // Add [i] to the set of ints -func (bs *BitSet) Add(i uint) { *bs |= 1 << i } +func (bs *BitSet64) Add(i uint) { *bs |= 1 << i } // Union adds all the elements in [s] to this set -func (bs *BitSet) Union(s BitSet) { *bs |= s } +func (bs *BitSet64) Union(s BitSet64) { *bs |= s } // Intersection takes the intersection of [s] with this set -func (bs *BitSet) Intersection(s BitSet) { *bs &= s } +func (bs *BitSet64) Intersection(s BitSet64) { *bs &= s } // Difference removes all the elements in [s] from this set -func (bs *BitSet) Difference(s BitSet) { *bs &^= s } +func (bs *BitSet64) Difference(s BitSet64) { *bs &^= s } // Remove [i] from the set of ints -func (bs *BitSet) Remove(i uint) { *bs &^= 1 << i } +func (bs *BitSet64) Remove(i uint) { *bs &^= 1 << i } // Clear removes all elements from this set -func (bs *BitSet) Clear() { *bs = 0 } +func (bs *BitSet64) Clear() { *bs = 0 } // Contains returns true if [i] was previously added to this set -func (bs BitSet) Contains(i uint) bool { return bs&(1< 0 { - element := qs.idList.Front() - head := element.Value.(ID) - if head == id { - return - } - qs.idList.Remove(element) - } - - qs.idList.PushFront(id) -} - -func (qs *QueueSet) Append(id ID) { - qs.init() - - qs.idList.PushBack(id) -} - -func (qs *QueueSet) GetTail() ID { - qs.init() - - if qs.idList.Len() == 0 { - return ID{} - } - return qs.idList.Back().Value.(ID) -} diff --git a/avalanchego/ids/queue_test.go b/avalanchego/ids/queue_test.go deleted file mode 100644 index 90d804bd..00000000 --- a/avalanchego/ids/queue_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "reflect" - "testing" -) - -func TestQueueSetinit(t *testing.T) { - qs := QueueSet{} - qs.init() - if qs.idList == nil { - t.Fatal("Failed to initialize") - } - list := qs.idList - qs.init() - if list != qs.idList { - t.Fatal("Mutated an already intialized queue") - } -} - -func TestQueueSetSetHead(t *testing.T) { - qs := QueueSet{} - id := ID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - qs.SetHead(id) - if qs.idList == nil || id != qs.idList.Front().Value.(ID) { - t.Fatal("Failed to set head of unintilised queue") - } - - qs.SetHead(id) - if qs.idList.Len() != 1 || id != qs.idList.Front().Value.(ID) { - t.Fatal("Mutated a queue which already had the desired head") - } - - id2 := ID{'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - qs.SetHead(id2) - if qs.idList.Len() != 1 || id2 != qs.idList.Front().Value.(ID) { - t.Fatal("Didn't replace the existing head") - } -} - -func TestQueueSetAppend(t *testing.T) { - qs := QueueSet{} - id := ID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - qs.Append(id) - if qs.idList == nil || id != qs.idList.Front().Value.(ID) { - t.Fatal("Failed to append to an uninitialised queue") - } - - id2 := ID{'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - qs.Append(id2) - if qs.idList.Len() != 2 || id2 != qs.idList.Back().Value.(ID) { - t.Fatal("Failed to append to the back of the queue") - } -} - -func TestQueueGetTail(t *testing.T) { - qs := QueueSet{} - tail := qs.GetTail() - if !reflect.DeepEqual(tail, ID{}) { - t.Fatalf("Empty queue returned %v, expected empty ID %v", tail, Empty) - } - - qs.Append(ID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}) - id2 := ID{'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - qs.Append(id2) - tail = qs.GetTail() - if tail != id2 { - t.Fatalf("Populated queue returned %v, expected %v", tail, id2) - } -} diff --git a/avalanchego/ids/set.go b/avalanchego/ids/set.go index 05454e48..9f9a1d8d 100644 --- a/avalanchego/ids/set.go +++ b/avalanchego/ids/set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/set_benchmark_test.go b/avalanchego/ids/set_benchmark_test.go index bc8d3099..b1db94e0 100644 --- a/avalanchego/ids/set_benchmark_test.go +++ b/avalanchego/ids/set_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/set_test.go b/avalanchego/ids/set_test.go index 4abc9bcb..22bf26ec 100644 --- a/avalanchego/ids/set_test.go +++ b/avalanchego/ids/set_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/short.go b/avalanchego/ids/short.go index c06357ba..244523f1 100644 --- a/avalanchego/ids/short.go +++ b/avalanchego/ids/short.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/short_set.go b/avalanchego/ids/short_set.go index d49263d1..4386e719 100644 --- a/avalanchego/ids/short_set.go +++ b/avalanchego/ids/short_set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/short_set_test.go b/avalanchego/ids/short_set_test.go index 66e5e434..beb4ee8e 100644 --- a/avalanchego/ids/short_set_test.go +++ b/avalanchego/ids/short_set_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/short_test.go b/avalanchego/ids/short_test.go index 3fee3b25..f0c90f56 100644 --- a/avalanchego/ids/short_test.go +++ b/avalanchego/ids/short_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/slice.go b/avalanchego/ids/slice.go index 812713d7..4013f72e 100644 --- a/avalanchego/ids/slice.go +++ b/avalanchego/ids/slice.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/test_aliases.go b/avalanchego/ids/test_aliases.go index d038c1dc..b9f8210e 100644 --- a/avalanchego/ids/test_aliases.go +++ b/avalanchego/ids/test_aliases.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/test_generator.go b/avalanchego/ids/test_generator.go index 24b8d87b..06ae5d33 100644 --- a/avalanchego/ids/test_generator.go +++ b/avalanchego/ids/test_generator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids diff --git a/avalanchego/ids/unique_bag.go b/avalanchego/ids/unique_bag.go index 668b885d..d0a95443 100644 --- a/avalanchego/ids/unique_bag.go +++ b/avalanchego/ids/unique_bag.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids @@ -12,16 +12,16 @@ const ( minUniqueBagSize = 16 ) -type UniqueBag map[ID]BitSet +type UniqueBag map[ID]BitSet64 func (b *UniqueBag) init() { if *b == nil { - *b = make(map[ID]BitSet, minUniqueBagSize) + *b = make(map[ID]BitSet64, minUniqueBagSize) } } func (b *UniqueBag) Add(setID uint, idSet ...ID) { - bs := BitSet(0) + bs := BitSet64(0) bs.Add(setID) for _, id := range idSet { @@ -29,7 +29,7 @@ func (b *UniqueBag) Add(setID uint, idSet ...ID) { } } -func (b *UniqueBag) UnionSet(id ID, set BitSet) { +func (b *UniqueBag) UnionSet(id ID, set BitSet64) { b.init() previousSet := (*b)[id] @@ -37,7 +37,7 @@ func (b *UniqueBag) UnionSet(id ID, set BitSet) { (*b)[id] = previousSet } -func (b *UniqueBag) DifferenceSet(id ID, set BitSet) { +func (b *UniqueBag) DifferenceSet(id ID, set BitSet64) { b.init() previousSet := (*b)[id] @@ -56,7 +56,7 @@ func (b *UniqueBag) Difference(diff *UniqueBag) { } } -func (b *UniqueBag) GetSet(id ID) BitSet { return (*b)[id] } +func (b *UniqueBag) GetSet(id ID) BitSet64 { return (*b)[id] } func (b *UniqueBag) RemoveSet(id ID) { delete(*b, id) } diff --git a/avalanchego/ids/unique_bag_test.go b/avalanchego/ids/unique_bag_test.go index 483de1c0..13221dcc 100644 --- a/avalanchego/ids/unique_bag_test.go +++ b/avalanchego/ids/unique_bag_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ids @@ -32,7 +32,7 @@ func TestUniqueBag(t *testing.T) { t.Fatalf("Set missing element") } - var bs1 BitSet + var bs1 BitSet64 bs1.Add(2) bs1.Add(4) @@ -94,7 +94,7 @@ func TestUniqueBag(t *testing.T) { ub6.Add(2, id1) ub6.Add(7, id1) - diffBitSet := BitSet(0) + diffBitSet := BitSet64(0) diffBitSet.Add(1) diffBitSet.Add(7) diff --git a/avalanchego/indexer/client.go b/avalanchego/indexer/client.go index dc0eb664..14fb84eb 100644 --- a/avalanchego/indexer/client.go +++ b/avalanchego/indexer/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/indexer/client_test.go b/avalanchego/indexer/client_test.go index 4a816201..1e0c6e89 100644 --- a/avalanchego/indexer/client_test.go +++ b/avalanchego/indexer/client_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/indexer/container.go b/avalanchego/indexer/container.go index 05508998..f4c03f87 100644 --- a/avalanchego/indexer/container.go +++ b/avalanchego/indexer/container.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/indexer/index.go b/avalanchego/indexer/index.go index c2275aa7..a51246c0 100644 --- a/avalanchego/indexer/index.go +++ b/avalanchego/indexer/index.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/indexer/index_test.go b/avalanchego/indexer/index_test.go index f293edcc..22e54465 100644 --- a/avalanchego/indexer/index_test.go +++ b/avalanchego/indexer/index_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/indexer/indexer.go b/avalanchego/indexer/indexer.go index d159f9a0..ad15e5b1 100644 --- a/avalanchego/indexer/indexer.go +++ b/avalanchego/indexer/indexer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/indexer/indexer_test.go b/avalanchego/indexer/indexer_test.go index 68124be1..fa67985d 100644 --- a/avalanchego/indexer/indexer_test.go +++ b/avalanchego/indexer/indexer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/indexer/service.go b/avalanchego/indexer/service.go index effa8c5d..664d7675 100644 --- a/avalanchego/indexer/service.go +++ b/avalanchego/indexer/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/ipcs/chainipc.go b/avalanchego/ipcs/chainipc.go index ee2d66fc..3ef1450d 100644 --- a/avalanchego/ipcs/chainipc.go +++ b/avalanchego/ipcs/chainipc.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ipcs diff --git a/avalanchego/ipcs/eventsocket.go b/avalanchego/ipcs/eventsocket.go index 093a49fd..0892b7d3 100644 --- a/avalanchego/ipcs/eventsocket.go +++ b/avalanchego/ipcs/eventsocket.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ipcs diff --git a/avalanchego/ipcs/socket/socket.go b/avalanchego/ipcs/socket/socket.go index 00273cee..786fb36d 100644 --- a/avalanchego/ipcs/socket/socket.go +++ b/avalanchego/ipcs/socket/socket.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package socket diff --git a/avalanchego/ipcs/socket/socket_test.go b/avalanchego/ipcs/socket/socket_test.go index 4b38e356..4baf0a4c 100644 --- a/avalanchego/ipcs/socket/socket_test.go +++ b/avalanchego/ipcs/socket/socket_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package socket diff --git a/avalanchego/ipcs/socket/socket_unix.go b/avalanchego/ipcs/socket/socket_unix.go index f9cb8986..578a29df 100644 --- a/avalanchego/ipcs/socket/socket_unix.go +++ b/avalanchego/ipcs/socket/socket_unix.go @@ -1,7 +1,7 @@ //go:build !windows && !plan9 && !js // +build !windows,!plan9,!js -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package socket diff --git a/avalanchego/ipcs/socket/socket_windows.go b/avalanchego/ipcs/socket/socket_windows.go index ca37edbc..3f034ae5 100644 --- a/avalanchego/ipcs/socket/socket_windows.go +++ b/avalanchego/ipcs/socket/socket_windows.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package socket diff --git a/avalanchego/main/main.go b/avalanchego/main/main.go index aa93d291..297eb06b 100644 --- a/avalanchego/main/main.go +++ b/avalanchego/main/main.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package main diff --git a/avalanchego/message/builder_test.go b/avalanchego/message/builder_test.go index 6520006b..7cb168cb 100644 --- a/avalanchego/message/builder_test.go +++ b/avalanchego/message/builder_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -33,8 +33,8 @@ func init() { panic(err) } TestCodec = codec - UncompressingBuilder = NewOutboundBuilder(codec, false /*compress*/) - TestInboundMsgBuilder = NewInboundBuilder(codec) + UncompressingBuilder = NewOutboundBuilderWithPacker(codec, false /*compress*/) + TestInboundMsgBuilder = NewInboundBuilderWithPacker(codec) } func TestBuildVersion(t *testing.T) { @@ -72,13 +72,34 @@ func TestBuildVersion(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, Version, parsedMsg.Op()) - require.EqualValues(t, networkID, parsedMsg.Get(NetworkID)) - require.EqualValues(t, myTime, parsedMsg.Get(MyTime)) - require.EqualValues(t, ip, parsedMsg.Get(IP)) - require.EqualValues(t, myVersionStr, parsedMsg.Get(VersionStr)) - require.EqualValues(t, myVersionTime, parsedMsg.Get(VersionTime)) - require.EqualValues(t, sig, parsedMsg.Get(SigBytes)) - require.EqualValues(t, subnetIDs, parsedMsg.Get(TrackedSubnets)) + + networkIDIntf, err := parsedMsg.Get(NetworkID) + require.NoError(t, err) + require.EqualValues(t, networkID, networkIDIntf) + + myTimeIntf, err := parsedMsg.Get(MyTime) + require.NoError(t, err) + require.EqualValues(t, myTime, myTimeIntf) + + ipIntf, err := parsedMsg.Get(IP) + require.NoError(t, err) + require.EqualValues(t, ip, ipIntf) + + versionStrIntf, err := parsedMsg.Get(VersionStr) + require.NoError(t, err) + require.EqualValues(t, myVersionStr, versionStrIntf) + + versionTimeIntf, err := parsedMsg.Get(VersionTime) + require.NoError(t, err) + require.EqualValues(t, myVersionTime, versionTimeIntf) + + sigBytesIntf, err := parsedMsg.Get(SigBytes) + require.NoError(t, err) + require.EqualValues(t, sig, sigBytesIntf) + + trackedSubnetsIntf, err := parsedMsg.Get(TrackedSubnets) + require.NoError(t, err) + require.EqualValues(t, subnetIDs, trackedSubnetsIntf) } func TestBuildGetAcceptedFrontier(t *testing.T) { @@ -95,9 +116,18 @@ func TestBuildGetAcceptedFrontier(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, GetAcceptedFrontier, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, deadline, parsedMsg.Get(Deadline)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + deadlineIntf, err := parsedMsg.Get(Deadline) + require.NoError(t, err) + require.Equal(t, deadline, deadlineIntf) } func TestBuildAcceptedFrontier(t *testing.T) { @@ -115,9 +145,18 @@ func TestBuildAcceptedFrontier(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, AcceptedFrontier, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, containerIDs, parsedMsg.Get(ContainerIDs)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + containerIDsIntf, err := parsedMsg.Get(ContainerIDs) + require.NoError(t, err) + require.Equal(t, containerIDs, containerIDsIntf) } func TestBuildGetAccepted(t *testing.T) { @@ -136,10 +175,22 @@ func TestBuildGetAccepted(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, GetAccepted, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, deadline, parsedMsg.Get(Deadline)) - require.Equal(t, containerIDs, parsedMsg.Get(ContainerIDs)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + deadlineIntf, err := parsedMsg.Get(Deadline) + require.NoError(t, err) + require.Equal(t, deadline, deadlineIntf) + + containerIDsIntf, err := parsedMsg.Get(ContainerIDs) + require.NoError(t, err) + require.Equal(t, containerIDs, containerIDsIntf) } func TestBuildAccepted(t *testing.T) { @@ -157,9 +208,18 @@ func TestBuildAccepted(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, Accepted, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, containerIDs, parsedMsg.Get(ContainerIDs)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + containerIDsIntf, err := parsedMsg.Get(ContainerIDs) + require.NoError(t, err) + require.Equal(t, containerIDs, containerIDsIntf) } func TestBuildGet(t *testing.T) { @@ -177,21 +237,32 @@ func TestBuildGet(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, Get, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, deadline, parsedMsg.Get(Deadline)) - require.Equal(t, containerID[:], parsedMsg.Get(ContainerID)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + deadlineIntf, err := parsedMsg.Get(Deadline) + require.NoError(t, err) + require.Equal(t, deadline, deadlineIntf) + + containerIDIntf, err := parsedMsg.Get(ContainerID) + require.NoError(t, err) + require.Equal(t, containerID[:], containerIDIntf) } func TestBuildPut(t *testing.T) { chainID := ids.Empty.Prefix(0) requestID := uint32(5) - containerID := ids.Empty.Prefix(1) container := []byte{2} for _, compress := range []bool{false, true} { - builder := NewOutboundBuilder(TestCodec, compress) - msg, err := builder.Put(chainID, requestID, containerID, container) + builder := NewOutboundBuilderWithPacker(TestCodec, compress) + msg, err := builder.Put(chainID, requestID, container) require.NoError(t, err) require.NotNil(t, msg) require.Equal(t, Put, msg.Op()) @@ -200,10 +271,22 @@ func TestBuildPut(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, Put, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, containerID[:], parsedMsg.Get(ContainerID)) - require.Equal(t, container, parsedMsg.Get(ContainerBytes)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + containerIDIntf, err := parsedMsg.Get(ContainerID) + require.NoError(t, err) + require.Equal(t, ids.Empty[:], containerIDIntf) + + containerIntf, err := parsedMsg.Get(ContainerBytes) + require.NoError(t, err) + require.Equal(t, container, containerIntf) } } @@ -211,12 +294,11 @@ func TestBuildPushQuery(t *testing.T) { chainID := ids.Empty.Prefix(0) requestID := uint32(5) deadline := uint64(15) - containerID := ids.Empty.Prefix(1) container := []byte{2} for _, compress := range []bool{false, true} { - builder := NewOutboundBuilder(TestCodec, compress) - msg, err := builder.PushQuery(chainID, requestID, time.Duration(deadline), containerID, container) + builder := NewOutboundBuilderWithPacker(TestCodec, compress) + msg, err := builder.PushQuery(chainID, requestID, time.Duration(deadline), container) require.NoError(t, err) require.NotNil(t, msg) require.Equal(t, PushQuery, msg.Op()) @@ -225,11 +307,26 @@ func TestBuildPushQuery(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, PushQuery, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, deadline, parsedMsg.Get(Deadline)) - require.Equal(t, containerID[:], parsedMsg.Get(ContainerID)) - require.Equal(t, container, parsedMsg.Get(ContainerBytes)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + deadlineIntf, err := parsedMsg.Get(Deadline) + require.NoError(t, err) + require.Equal(t, deadline, deadlineIntf) + + containerIDIntf, err := parsedMsg.Get(ContainerID) + require.NoError(t, err) + require.Equal(t, ids.Empty[:], containerIDIntf) + + containerIntf, err := parsedMsg.Get(ContainerBytes) + require.NoError(t, err) + require.Equal(t, container, containerIntf) } } @@ -248,10 +345,22 @@ func TestBuildPullQuery(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, PullQuery, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, deadline, parsedMsg.Get(Deadline)) - require.Equal(t, containerID[:], parsedMsg.Get(ContainerID)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + deadlineIntf, err := parsedMsg.Get(Deadline) + require.NoError(t, err) + require.Equal(t, deadline, deadlineIntf) + + containerIDIntf, err := parsedMsg.Get(ContainerID) + require.NoError(t, err) + require.Equal(t, containerID[:], containerIDIntf) } func TestBuildChits(t *testing.T) { @@ -269,38 +378,18 @@ func TestBuildChits(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, Chits, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, containerIDs, parsedMsg.Get(ContainerIDs)) -} -func TestBuildChitsV2(t *testing.T) { - chainID := ids.Empty.Prefix(0) - requestID := uint32(5) - containerID := ids.Empty.Prefix(1) - containerIDs := [][]byte{containerID[:]} - - msg := TestInboundMsgBuilder.InboundChitsV2(chainID, requestID, []ids.ID{containerID}, containerID, dummyNodeID) - require.NotNil(t, msg) - require.Equal(t, ChitsV2, msg.Op()) - require.Equal(t, chainID[:], msg.Get(ChainID)) - require.Equal(t, requestID, msg.Get(RequestID)) - require.Equal(t, containerIDs, msg.Get(ContainerIDs)) - require.Equal(t, containerID[:], msg.Get(ContainerID)) + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) - outboundMsg, err := UncompressingBuilder.ChitsV2(chainID, requestID, []ids.ID{containerID}, containerID) + requestIDIntf, err := parsedMsg.Get(RequestID) require.NoError(t, err) - require.NotNil(t, outboundMsg) - require.Equal(t, ChitsV2, outboundMsg.Op()) + require.Equal(t, requestID, requestIDIntf) - parsedMsg, err := TestCodec.Parse(outboundMsg.Bytes(), dummyNodeID, dummyOnFinishedHandling) + containerIDsIntf, err := parsedMsg.Get(ContainerIDs) require.NoError(t, err) - require.NotNil(t, parsedMsg) - require.Equal(t, ChitsV2, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, containerIDs, parsedMsg.Get(ContainerIDs)) - require.Equal(t, containerID[:], parsedMsg.Get(ContainerID)) + require.Equal(t, containerIDs, containerIDsIntf) } func TestBuildAncestors(t *testing.T) { @@ -311,7 +400,7 @@ func TestBuildAncestors(t *testing.T) { containers := [][]byte{container[:], container2[:]} for _, compress := range []bool{false, true} { - builder := NewOutboundBuilder(TestCodec, compress) + builder := NewOutboundBuilderWithPacker(TestCodec, compress) msg, err := builder.Ancestors(chainID, requestID, containers) require.NoError(t, err) require.NotNil(t, msg) @@ -321,9 +410,18 @@ func TestBuildAncestors(t *testing.T) { require.NoError(t, err) require.NotNil(t, parsedMsg) require.Equal(t, Ancestors, parsedMsg.Op()) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) - require.Equal(t, requestID, parsedMsg.Get(RequestID)) - require.Equal(t, containers, parsedMsg.Get(MultiContainerBytes)) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.Equal(t, requestID, requestIDIntf) + + multiContainerBytesIntf, err := parsedMsg.Get(MultiContainerBytes) + require.NoError(t, err) + require.Equal(t, containers, multiContainerBytesIntf) } } @@ -335,7 +433,7 @@ func TestBuildAppRequestMsg(t *testing.T) { deadline := uint64(time.Now().Unix()) for _, compress := range []bool{false, true} { - builder := NewOutboundBuilder(TestCodec, compress) + builder := NewOutboundBuilderWithPacker(TestCodec, compress) msg, err := builder.AppRequest(chainID, 1, time.Duration(deadline), appRequestBytes) require.NoError(t, err) require.NotNil(t, msg) @@ -355,7 +453,7 @@ func TestBuildAppResponseMsg(t *testing.T) { appResponseBytes[len(appResponseBytes)-1] = 1 for _, compress := range []bool{false, true} { - builder := NewOutboundBuilder(TestCodec, compress) + builder := NewOutboundBuilderWithPacker(TestCodec, compress) msg, err := builder.AppResponse(chainID, 1, appResponseBytes) require.NoError(t, err) require.NotNil(t, msg) @@ -365,9 +463,18 @@ func TestBuildAppResponseMsg(t *testing.T) { require.NoError(t, err) require.NotNil(t, msg) require.Equal(t, AppResponse, msg.Op()) - require.EqualValues(t, 1, parsedMsg.Get(RequestID)) - require.Equal(t, appResponseBytes, parsedMsg.Get(AppBytes)) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) + + requestIDIntf, err := parsedMsg.Get(RequestID) + require.NoError(t, err) + require.EqualValues(t, 1, requestIDIntf) + + appBytesIntf, err := parsedMsg.Get(AppBytes) + require.NoError(t, err) + require.Equal(t, appResponseBytes, appBytesIntf) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) } } @@ -378,7 +485,7 @@ func TestBuildAppGossipMsg(t *testing.T) { appGossipBytes[len(appGossipBytes)-1] = 1 for _, compress := range []bool{false, true} { - testBuilder := NewOutboundBuilder(TestCodec, compress) + testBuilder := NewOutboundBuilderWithPacker(TestCodec, compress) msg, err := testBuilder.AppGossip(chainID, appGossipBytes) require.NoError(t, err) require.NotNil(t, msg) @@ -388,7 +495,13 @@ func TestBuildAppGossipMsg(t *testing.T) { require.NoError(t, err) require.NotNil(t, msg) require.Equal(t, AppGossip, msg.Op()) - require.Equal(t, appGossipBytes, parsedMsg.Get(AppBytes)) - require.Equal(t, chainID[:], parsedMsg.Get(ChainID)) + + appBytesIntf, err := parsedMsg.Get(AppBytes) + require.NoError(t, err) + require.Equal(t, appGossipBytes, appBytesIntf) + + chainIDIntf, err := parsedMsg.Get(ChainID) + require.NoError(t, err) + require.Equal(t, chainID[:], chainIDIntf) } } diff --git a/avalanchego/message/codec.go b/avalanchego/message/codec.go index e55215dc..182ad434 100644 --- a/avalanchego/message/codec.go +++ b/avalanchego/message/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -65,6 +65,10 @@ type codec struct { } func NewCodecWithMemoryPool(namespace string, metrics prometheus.Registerer, maxMessageSize int64, maxMessageTimeout time.Duration) (Codec, error) { + cpr, err := compression.NewGzipCompressor(maxMessageSize) + if err != nil { + return nil, err + } c := &codec{ byteSlicePool: sync.Pool{ New: func() interface{} { @@ -73,7 +77,7 @@ func NewCodecWithMemoryPool(namespace string, metrics prometheus.Registerer, max }, compressTimeMetrics: make(map[Op]metric.Averager, len(ExternalOps)), decompressTimeMetrics: make(map[Op]metric.Averager, len(ExternalOps)), - compressor: compression.NewGzipCompressor(maxMessageSize), + compressor: cpr, maxMessageTimeout: maxMessageTimeout, } @@ -147,12 +151,15 @@ func (c *codec) Pack( if p.Err != nil { return nil, p.Err } - msg := &outboundMessage{ - op: op, - bytes: p.Bytes, - refs: 1, - c: c, - bypassThrottling: bypassThrottling, + msg := &outboundMessageWithPacker{ + outboundMessage: outboundMessage{ + op: op, + bytes: p.Bytes, + bypassThrottling: bypassThrottling, + }, + + refs: 1, + c: c, } if !compress { return msg, nil @@ -244,12 +251,14 @@ func (c *codec) Parse(bytes []byte, nodeID ids.NodeID, onFinishedHandling func() expirationTime = c.clock.Time().Add(deadlineDuration) } - return &inboundMessage{ - op: op, - fields: fieldValues, - bytesSavedCompression: bytesSaved, - nodeID: nodeID, - expirationTime: expirationTime, - onFinishedHandling: onFinishedHandling, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: op, + bytesSavedCompression: bytesSaved, + nodeID: nodeID, + expirationTime: expirationTime, + onFinishedHandling: onFinishedHandling, + }, + fields: fieldValues, }, nil } diff --git a/avalanchego/message/codec_test.go b/avalanchego/message/codec_test.go index cd5dc9ca..190d7184 100644 --- a/avalanchego/message/codec_test.go +++ b/avalanchego/message/codec_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -65,8 +65,10 @@ func TestDeadlineOverride(t *testing.T) { require.NoError(t, err) id := ids.GenerateTestID() - m := inboundMessage{ - op: PushQuery, + m := inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: PushQuery, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -82,7 +84,7 @@ func TestDeadlineOverride(t *testing.T) { unpackedIntf, err := c.Parse(packedIntf.Bytes(), dummyNodeID, dummyOnFinishedHandling) require.NoError(t, err, "failed to parse w/ compression on operation %s", m.op) - unpacked := unpackedIntf.(*inboundMessage) + unpacked := unpackedIntf.(*inboundMessageWithPacker) require.NotEqual(t, unpacked.ExpirationTime(), time.Now().Add(1337*time.Hour)) require.True(t, time.Since(unpacked.ExpirationTime()) <= 10*time.Second) } @@ -98,9 +100,11 @@ func TestCodecPackParseGzip(t *testing.T) { require.NoError(t, err) cert := tlsCert.Leaf - msgs := []inboundMessage{ + msgs := []inboundMessageWithPacker{ { - op: Version, + inboundMessage: inboundMessage{ + op: Version, + }, fields: map[Field]interface{}{ NetworkID: uint32(0), NodeID: uint32(1337), @@ -113,7 +117,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: PeerList, + inboundMessage: inboundMessage{ + op: PeerList, + }, fields: map[Field]interface{}{ Peers: []ips.ClaimedIPPort{ { @@ -126,17 +132,23 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: Ping, + inboundMessage: inboundMessage{ + op: Ping, + }, fields: map[Field]interface{}{}, }, { - op: Pong, + inboundMessage: inboundMessage{ + op: Pong, + }, fields: map[Field]interface{}{ Uptime: uint8(80), }, }, { - op: GetAcceptedFrontier, + inboundMessage: inboundMessage{ + op: GetAcceptedFrontier, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -144,7 +156,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: AcceptedFrontier, + inboundMessage: inboundMessage{ + op: AcceptedFrontier, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -152,7 +166,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: GetAccepted, + inboundMessage: inboundMessage{ + op: GetAccepted, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -161,7 +177,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: Accepted, + inboundMessage: inboundMessage{ + op: Accepted, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -169,7 +187,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: Ancestors, + inboundMessage: inboundMessage{ + op: Ancestors, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -177,7 +197,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: Get, + inboundMessage: inboundMessage{ + op: Get, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -186,7 +208,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: Put, + inboundMessage: inboundMessage{ + op: Put, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -195,7 +219,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: PushQuery, + inboundMessage: inboundMessage{ + op: PushQuery, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -205,7 +231,9 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: PullQuery, + inboundMessage: inboundMessage{ + op: PullQuery, + }, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), @@ -214,20 +242,13 @@ func TestCodecPackParseGzip(t *testing.T) { }, }, { - op: Chits, - fields: map[Field]interface{}{ - ChainID: id[:], - RequestID: uint32(1337), - ContainerIDs: [][]byte{id[:]}, + inboundMessage: inboundMessage{ + op: Chits, }, - }, - { - op: ChitsV2, fields: map[Field]interface{}{ ChainID: id[:], RequestID: uint32(1337), ContainerIDs: [][]byte{id[:]}, - ContainerID: id[:], }, }, } @@ -238,7 +259,7 @@ func TestCodecPackParseGzip(t *testing.T) { unpackedIntf, err := c.Parse(packedIntf.Bytes(), dummyNodeID, dummyOnFinishedHandling) require.NoError(t, err, "failed to parse w/ compression on operation %s", m.op) - unpacked := unpackedIntf.(*inboundMessage) + unpacked := unpackedIntf.(*inboundMessageWithPacker) require.EqualValues(t, len(m.fields), len(unpacked.fields)) } diff --git a/avalanchego/message/creator.go b/avalanchego/message/creator.go index 9392a7a8..059c5ec9 100644 --- a/avalanchego/message/creator.go +++ b/avalanchego/message/creator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -26,15 +26,29 @@ type creator struct { InternalMsgBuilder } -func NewCreator(metrics prometheus.Registerer, compressionEnabled bool, parentNamespace string, maxInboundMessageTimeout time.Duration) (Creator, error) { +func NewCreator(metrics prometheus.Registerer, parentNamespace string, compressionEnabled bool, maxInboundMessageTimeout time.Duration) (Creator, error) { namespace := fmt.Sprintf("%s_codec", parentNamespace) codec, err := NewCodecWithMemoryPool(namespace, metrics, int64(constants.DefaultMaxMessageSize), maxInboundMessageTimeout) if err != nil { return nil, err } return &creator{ - OutboundMsgBuilder: NewOutboundBuilder(codec, compressionEnabled), - InboundMsgBuilder: NewInboundBuilder(codec), + OutboundMsgBuilder: NewOutboundBuilderWithPacker(codec, compressionEnabled), + InboundMsgBuilder: NewInboundBuilderWithPacker(codec), + InternalMsgBuilder: NewInternalBuilder(), + }, nil +} + +func NewCreatorWithProto(metrics prometheus.Registerer, parentNamespace string, compressionEnabled bool, maxInboundMessageTimeout time.Duration) (Creator, error) { + // different namespace, not to be in conflict with packer + namespace := fmt.Sprintf("%s_proto_codec", parentNamespace) + builder, err := newMsgBuilderProtobuf(namespace, metrics, int64(constants.DefaultMaxMessageSize), maxInboundMessageTimeout) + if err != nil { + return nil, err + } + return &creator{ + OutboundMsgBuilder: newOutboundBuilderWithProto(compressionEnabled, builder), + InboundMsgBuilder: newInboundBuilderWithProto(builder), InternalMsgBuilder: NewInternalBuilder(), }, nil } diff --git a/avalanchego/message/fields.go b/avalanchego/message/fields.go index 9637ede4..d1b8d248 100644 --- a/avalanchego/message/fields.go +++ b/avalanchego/message/fields.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message diff --git a/avalanchego/message/inbound_msg_builder.go b/avalanchego/message/inbound_msg_builder.go index 243758aa..9dcd99e5 100644 --- a/avalanchego/message/inbound_msg_builder.go +++ b/avalanchego/message/inbound_msg_builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -10,7 +10,7 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" ) -var _ InboundMsgBuilder = &inMsgBuilder{} +var _ InboundMsgBuilder = &inMsgBuilderWithPacker{} type InboundMsgBuilder interface { Parser @@ -84,7 +84,6 @@ type InboundMsgBuilder interface { chainID ids.ID, requestID uint32, deadline time.Duration, - containerID ids.ID, container []byte, nodeID ids.NodeID, ) InboundMessage @@ -104,14 +103,6 @@ type InboundMsgBuilder interface { nodeID ids.NodeID, ) InboundMessage - InboundChitsV2( - chainID ids.ID, - requestID uint32, - containerIDs []ids.ID, - containerID ids.ID, - nodeID ids.NodeID, - ) InboundMessage - InboundAppRequest( chainID ids.ID, requestID uint32, @@ -138,65 +129,68 @@ type InboundMsgBuilder interface { InboundPut( chainID ids.ID, requestID uint32, - containerID ids.ID, container []byte, nodeID ids.NodeID, ) InboundMessage // used in UTs only } -type inMsgBuilder struct { +type inMsgBuilderWithPacker struct { Codec clock mockable.Clock } -func NewInboundBuilder(c Codec) InboundMsgBuilder { - return &inMsgBuilder{ +func NewInboundBuilderWithPacker(c Codec) InboundMsgBuilder { + return &inMsgBuilderWithPacker{ Codec: c, } } -func (b *inMsgBuilder) SetTime(t time.Time) { +func (b *inMsgBuilderWithPacker) SetTime(t time.Time) { b.clock.Set(t) b.Codec.SetTime(t) } -func (b *inMsgBuilder) InboundGetStateSummaryFrontier( +func (b *inMsgBuilderWithPacker) InboundGetStateSummaryFrontier( chainID ids.ID, requestID uint32, deadline time.Duration, nodeID ids.NodeID, ) InboundMessage { received := b.clock.Time() - return &inboundMessage{ - op: GetStateSummaryFrontier, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: GetStateSummaryFrontier, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundStateSummaryFrontier( +func (b *inMsgBuilderWithPacker) InboundStateSummaryFrontier( chainID ids.ID, requestID uint32, summary []byte, nodeID ids.NodeID, ) InboundMessage { - return &inboundMessage{ - op: StateSummaryFrontier, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: StateSummaryFrontier, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, SummaryBytes: summary, }, - nodeID: nodeID, } } -func (b *inMsgBuilder) InboundGetAcceptedStateSummary( +func (b *inMsgBuilderWithPacker) InboundGetAcceptedStateSummary( chainID ids.ID, requestID uint32, heights []uint64, @@ -204,20 +198,22 @@ func (b *inMsgBuilder) InboundGetAcceptedStateSummary( nodeID ids.NodeID, ) InboundMessage { received := b.clock.Time() - return &inboundMessage{ - op: GetAcceptedStateSummary, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: GetAcceptedStateSummary, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), SummaryHeights: heights, }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundAcceptedStateSummary( +func (b *inMsgBuilderWithPacker) InboundAcceptedStateSummary( chainID ids.ID, requestID uint32, summaryIDs []ids.ID, @@ -225,37 +221,41 @@ func (b *inMsgBuilder) InboundAcceptedStateSummary( ) InboundMessage { summaryIDBytes := make([][]byte, len(summaryIDs)) encodeIDs(summaryIDs, summaryIDBytes) - return &inboundMessage{ - op: AcceptedStateSummary, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: AcceptedStateSummary, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, SummaryIDs: summaryIDBytes, }, - nodeID: nodeID, } } -func (b *inMsgBuilder) InboundGetAcceptedFrontier( +func (b *inMsgBuilderWithPacker) InboundGetAcceptedFrontier( chainID ids.ID, requestID uint32, deadline time.Duration, nodeID ids.NodeID, ) InboundMessage { received := b.clock.Time() - return &inboundMessage{ - op: GetAcceptedFrontier, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: GetAcceptedFrontier, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundAcceptedFrontier( +func (b *inMsgBuilderWithPacker) InboundAcceptedFrontier( chainID ids.ID, requestID uint32, containerIDs []ids.ID, @@ -263,18 +263,20 @@ func (b *inMsgBuilder) InboundAcceptedFrontier( ) InboundMessage { containerIDBytes := make([][]byte, len(containerIDs)) encodeIDs(containerIDs, containerIDBytes) - return &inboundMessage{ - op: AcceptedFrontier, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: AcceptedFrontier, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, ContainerIDs: containerIDBytes, }, - nodeID: nodeID, } } -func (b *inMsgBuilder) InboundGetAccepted( +func (b *inMsgBuilderWithPacker) InboundGetAccepted( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -284,20 +286,22 @@ func (b *inMsgBuilder) InboundGetAccepted( received := b.clock.Time() containerIDBytes := make([][]byte, len(containerIDs)) encodeIDs(containerIDs, containerIDBytes) - return &inboundMessage{ - op: GetAccepted, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: GetAccepted, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), ContainerIDs: containerIDBytes, }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundAccepted( +func (b *inMsgBuilderWithPacker) InboundAccepted( chainID ids.ID, requestID uint32, containerIDs []ids.ID, @@ -305,41 +309,43 @@ func (b *inMsgBuilder) InboundAccepted( ) InboundMessage { containerIDBytes := make([][]byte, len(containerIDs)) encodeIDs(containerIDs, containerIDBytes) - return &inboundMessage{ - op: Accepted, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Accepted, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, ContainerIDs: containerIDBytes, }, - nodeID: nodeID, } } -func (b *inMsgBuilder) InboundPushQuery( +func (b *inMsgBuilderWithPacker) InboundPushQuery( chainID ids.ID, requestID uint32, deadline time.Duration, - containerID ids.ID, container []byte, nodeID ids.NodeID, ) InboundMessage { received := b.clock.Time() - return &inboundMessage{ - op: PushQuery, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: PushQuery, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), - ContainerID: containerID[:], ContainerBytes: container, }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundPullQuery( +func (b *inMsgBuilderWithPacker) InboundPullQuery( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -347,20 +353,22 @@ func (b *inMsgBuilder) InboundPullQuery( nodeID ids.NodeID, ) InboundMessage { received := b.clock.Time() - return &inboundMessage{ - op: PullQuery, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: PullQuery, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), ContainerID: containerID[:], }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundChits( +func (b *inMsgBuilderWithPacker) InboundChits( chainID ids.ID, requestID uint32, containerIDs []ids.ID, @@ -368,43 +376,20 @@ func (b *inMsgBuilder) InboundChits( ) InboundMessage { containerIDBytes := make([][]byte, len(containerIDs)) encodeIDs(containerIDs, containerIDBytes) - return &inboundMessage{ - op: Chits, - fields: map[Field]interface{}{ - ChainID: chainID[:], - RequestID: requestID, - ContainerIDs: containerIDBytes, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Chits, + nodeID: nodeID, }, - nodeID: nodeID, - } -} - -// The first "containerIDs" is always populated for backward compatibilities with old "Chits" -// Only after the DAG is linearized, the second "containerID" will be populated -// with the new snowman chain containers. -func (b *inMsgBuilder) InboundChitsV2( - chainID ids.ID, - requestID uint32, - containerIDs []ids.ID, - containerID ids.ID, - nodeID ids.NodeID, -) InboundMessage { - containerIDBytes := make([][]byte, len(containerIDs)) - encodeIDs(containerIDs, containerIDBytes) - - return &inboundMessage{ - op: ChitsV2, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, ContainerIDs: containerIDBytes, - ContainerID: containerID[:], }, - nodeID: nodeID, } } -func (b *inMsgBuilder) InboundAppRequest( +func (b *inMsgBuilderWithPacker) InboundAppRequest( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -412,37 +397,41 @@ func (b *inMsgBuilder) InboundAppRequest( nodeID ids.NodeID, ) InboundMessage { received := b.clock.Time() - return &inboundMessage{ - op: AppRequest, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: AppRequest, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), AppBytes: msg, }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundAppResponse( +func (b *inMsgBuilderWithPacker) InboundAppResponse( chainID ids.ID, requestID uint32, msg []byte, nodeID ids.NodeID, ) InboundMessage { - return &inboundMessage{ - op: AppResponse, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: AppResponse, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, AppBytes: msg, }, - nodeID: nodeID, } } -func (b *inMsgBuilder) InboundGet( +func (b *inMsgBuilderWithPacker) InboundGet( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -450,52 +439,56 @@ func (b *inMsgBuilder) InboundGet( nodeID ids.NodeID, ) InboundMessage { // used in UTs only received := b.clock.Time() - return &inboundMessage{ - op: Put, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Put, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), ContainerID: containerID[:], }, - nodeID: nodeID, - expirationTime: received.Add(deadline), } } -func (b *inMsgBuilder) InboundPut( +func (b *inMsgBuilderWithPacker) InboundPut( chainID ids.ID, requestID uint32, - containerID ids.ID, container []byte, nodeID ids.NodeID, ) InboundMessage { // used in UTs only - return &inboundMessage{ - op: Put, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Put, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, - ContainerID: containerID[:], ContainerBytes: container, }, - nodeID: nodeID, } } -func (b *inMsgBuilder) InboundAncestors( +func (b *inMsgBuilderWithPacker) InboundAncestors( chainID ids.ID, requestID uint32, containers [][]byte, nodeID ids.NodeID, ) InboundMessage { // used in UTs only - return &inboundMessage{ - op: Ancestors, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Ancestors, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, MultiContainerBytes: containers, }, - nodeID: nodeID, } } diff --git a/avalanchego/message/inbound_msg_builder_proto.go b/avalanchego/message/inbound_msg_builder_proto.go new file mode 100644 index 00000000..c3681755 --- /dev/null +++ b/avalanchego/message/inbound_msg_builder_proto.go @@ -0,0 +1,439 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/proto/pb/p2p" +) + +var _ InboundMsgBuilder = &inMsgBuilderWithProto{} + +type inMsgBuilderWithProto struct { + protoBuilder *msgBuilderProtobuf +} + +// Use "message.NewCreatorWithProto" to import this function +// since we do not expose "msgBuilderProtobuf" yet +func newInboundBuilderWithProto(protoBuilder *msgBuilderProtobuf) InboundMsgBuilder { + return &inMsgBuilderWithProto{ + protoBuilder: protoBuilder, + } +} + +func (b *inMsgBuilderWithProto) SetTime(t time.Time) { + b.protoBuilder.clock.Set(t) +} + +func (b *inMsgBuilderWithProto) Parse(bytes []byte, nodeID ids.NodeID, onFinishedHandling func()) (InboundMessage, error) { + return b.protoBuilder.parseInbound(bytes, nodeID, onFinishedHandling) +} + +func (b *inMsgBuilderWithProto) InboundGetStateSummaryFrontier( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + nodeID ids.NodeID, +) InboundMessage { + received := b.protoBuilder.clock.Time() + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: GetStateSummaryFrontier, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_GetStateSummaryFrontier{ + GetStateSummaryFrontier: &p2p.GetStateSummaryFrontier{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundStateSummaryFrontier( + chainID ids.ID, + requestID uint32, + summary []byte, + nodeID ids.NodeID, +) InboundMessage { + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: StateSummaryFrontier, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_StateSummaryFrontier_{ + StateSummaryFrontier_: &p2p.StateSummaryFrontier{ + ChainId: chainID[:], + RequestId: requestID, + Summary: summary, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundGetAcceptedStateSummary( + chainID ids.ID, + requestID uint32, + heights []uint64, + deadline time.Duration, + nodeID ids.NodeID, +) InboundMessage { + received := b.protoBuilder.clock.Time() + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: GetAcceptedStateSummary, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_GetAcceptedStateSummary{ + GetAcceptedStateSummary: &p2p.GetAcceptedStateSummary{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + Heights: heights, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundAcceptedStateSummary( + chainID ids.ID, + requestID uint32, + summaryIDs []ids.ID, + nodeID ids.NodeID, +) InboundMessage { + summaryIDBytes := make([][]byte, len(summaryIDs)) + encodeIDs(summaryIDs, summaryIDBytes) + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: AcceptedStateSummary, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_AcceptedStateSummary_{ + AcceptedStateSummary_: &p2p.AcceptedStateSummary{ + ChainId: chainID[:], + RequestId: requestID, + SummaryIds: summaryIDBytes, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundGetAcceptedFrontier( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + nodeID ids.NodeID, +) InboundMessage { + received := b.protoBuilder.clock.Time() + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: GetAcceptedFrontier, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_GetAcceptedFrontier{ + GetAcceptedFrontier: &p2p.GetAcceptedFrontier{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundAcceptedFrontier( + chainID ids.ID, + requestID uint32, + containerIDs []ids.ID, + nodeID ids.NodeID, +) InboundMessage { + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: AcceptedFrontier, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_AcceptedFrontier_{ + AcceptedFrontier_: &p2p.AcceptedFrontier{ + ChainId: chainID[:], + RequestId: requestID, + ContainerIds: containerIDBytes, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundGetAccepted( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + containerIDs []ids.ID, + nodeID ids.NodeID, +) InboundMessage { + received := b.protoBuilder.clock.Time() + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: GetAccepted, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_GetAccepted{ + GetAccepted: &p2p.GetAccepted{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + ContainerIds: containerIDBytes, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundAccepted( + chainID ids.ID, + requestID uint32, + containerIDs []ids.ID, + nodeID ids.NodeID, +) InboundMessage { + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: Accepted, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_Accepted_{ + Accepted_: &p2p.Accepted{ + ChainId: chainID[:], + RequestId: requestID, + ContainerIds: containerIDBytes, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundPushQuery( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + container []byte, + nodeID ids.NodeID, +) InboundMessage { + received := b.protoBuilder.clock.Time() + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: PushQuery, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_PushQuery{ + PushQuery: &p2p.PushQuery{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + Container: container, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundPullQuery( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + containerID ids.ID, + nodeID ids.NodeID, +) InboundMessage { + received := b.protoBuilder.clock.Time() + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: PullQuery, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_PullQuery{ + PullQuery: &p2p.PullQuery{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + ContainerId: containerID[:], + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundChits( + chainID ids.ID, + requestID uint32, + containerIDs []ids.ID, + nodeID ids.NodeID, +) InboundMessage { + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: Chits, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_Chits{ + Chits: &p2p.Chits{ + ChainId: chainID[:], + RequestId: requestID, + ContainerIds: containerIDBytes, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundAppRequest( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + msg []byte, + nodeID ids.NodeID, +) InboundMessage { + received := b.protoBuilder.clock.Time() + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: AppRequest, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_AppRequest{ + AppRequest: &p2p.AppRequest{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + AppBytes: msg, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundAppResponse( + chainID ids.ID, + requestID uint32, + msg []byte, + nodeID ids.NodeID, +) InboundMessage { + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: AppResponse, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_AppResponse{ + AppResponse: &p2p.AppResponse{ + ChainId: chainID[:], + RequestId: requestID, + AppBytes: msg, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundGet( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + containerID ids.ID, + nodeID ids.NodeID, +) InboundMessage { // used in UTs only + received := b.protoBuilder.clock.Time() + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: Get, + nodeID: nodeID, + expirationTime: received.Add(deadline), + }, + msg: &p2p.Message{ + Message: &p2p.Message_Get{ + Get: &p2p.Get{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + ContainerId: containerID[:], + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundPut( + chainID ids.ID, + requestID uint32, + container []byte, + nodeID ids.NodeID, +) InboundMessage { // used in UTs only + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: Put, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_Put{ + Put: &p2p.Put{ + ChainId: chainID[:], + RequestId: requestID, + Container: container, + }, + }, + }, + } +} + +func (b *inMsgBuilderWithProto) InboundAncestors( + chainID ids.ID, + requestID uint32, + containers [][]byte, + nodeID ids.NodeID, +) InboundMessage { // used in UTs only + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: Ancestors, + nodeID: nodeID, + }, + msg: &p2p.Message{ + Message: &p2p.Message_Ancestors_{ + Ancestors_: &p2p.Ancestors{ + ChainId: chainID[:], + RequestId: requestID, + Containers: containers, + }, + }, + }, + } +} diff --git a/avalanchego/message/inbound_msg_builder_proto_test.go b/avalanchego/message/inbound_msg_builder_proto_test.go new file mode 100644 index 00000000..a29bf2c2 --- /dev/null +++ b/avalanchego/message/inbound_msg_builder_proto_test.go @@ -0,0 +1,35 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" +) + +func Test_newInboundBuilderWithProto(t *testing.T) { + t.Parallel() + require := require.New(t) + + mb, err := newMsgBuilderProtobuf("test", prometheus.NewRegistry(), int64(constants.DefaultMaxMessageSize), 5*time.Second) + require.NoError(err) + + builder := newInboundBuilderWithProto(mb) + + inMsg := builder.InboundAccepted( + ids.GenerateTestID(), + uint32(12345), + []ids.ID{ids.GenerateTestID()}, + ids.GenerateTestNodeID(), + ) + + t.Logf("outbound message built %q", inMsg.Op().String()) +} diff --git a/avalanchego/message/internal_msg_builder.go b/avalanchego/message/internal_msg_builder.go index a5f25c9b..3f988eed 100644 --- a/avalanchego/message/internal_msg_builder.go +++ b/avalanchego/message/internal_msg_builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -37,37 +37,45 @@ func (internalMsgBuilder) InternalFailedRequest( chainID ids.ID, requestID uint32, ) InboundMessage { - return &inboundMessage{ - op: op, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: op, + nodeID: nodeID, + }, fields: map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, }, - nodeID: nodeID, } } func (internalMsgBuilder) InternalTimeout(nodeID ids.NodeID) InboundMessage { - return &inboundMessage{ - op: Timeout, - nodeID: nodeID, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Timeout, + nodeID: nodeID, + }, } } func (internalMsgBuilder) InternalConnected(nodeID ids.NodeID, nodeVersion *version.Application) InboundMessage { - return &inboundMessage{ - op: Connected, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Connected, + nodeID: nodeID, + }, fields: map[Field]interface{}{ VersionStruct: nodeVersion, }, - nodeID: nodeID, } } func (internalMsgBuilder) InternalDisconnected(nodeID ids.NodeID) InboundMessage { - return &inboundMessage{ - op: Disconnected, - nodeID: nodeID, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Disconnected, + nodeID: nodeID, + }, } } @@ -75,20 +83,24 @@ func (internalMsgBuilder) InternalVMMessage( nodeID ids.NodeID, notification uint32, ) InboundMessage { - return &inboundMessage{ - op: Notify, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Notify, + nodeID: nodeID, + }, fields: map[Field]interface{}{ VMMessage: notification, }, - nodeID: nodeID, } } func (internalMsgBuilder) InternalGossipRequest( nodeID ids.NodeID, ) InboundMessage { - return &inboundMessage{ - op: GossipRequest, - nodeID: nodeID, + return &inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: GossipRequest, + nodeID: nodeID, + }, } } diff --git a/avalanchego/message/messages.go b/avalanchego/message/messages.go index b148008e..0fc2d5f9 100644 --- a/avalanchego/message/messages.go +++ b/avalanchego/message/messages.go @@ -1,20 +1,42 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message import ( + "crypto/x509" + "errors" "fmt" + "net" "strings" "sync" "time" + "github.com/prometheus/client_golang/prometheus" + + "google.golang.org/protobuf/proto" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/compression" + "github.com/ava-labs/avalanchego/utils/ips" + "github.com/ava-labs/avalanchego/utils/metric" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/utils/wrappers" + + p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p" ) var ( - _ InboundMessage = &inboundMessage{} - _ OutboundMessage = &outboundMessage{} + _ InboundMessage = &inboundMessageWithPacker{} + _ InboundMessage = &inboundMessageWithProto{} + _ OutboundMessage = &outboundMessageWithPacker{} + _ OutboundMessage = &outboundMessageWithProto{} + + errUnknownMessageTypeForOp = errors.New("unknown message type for Op") + errUnexpectedCompressedOp = errors.New("unexpected compressed Op") + + errInvalidIPAddrLen = errors.New("invalid IP address field length (expected 16-byte)") + errInvalidCert = errors.New("invalid TLS certificate field") ) // InboundMessage represents a set of fields for an inbound message that can be serialized into a byte stream @@ -23,7 +45,7 @@ type InboundMessage interface { BytesSavedCompression() int Op() Op - Get(Field) interface{} + Get(Field) (interface{}, error) NodeID() ids.NodeID ExpirationTime() time.Time OnFinishedHandling() @@ -32,7 +54,6 @@ type InboundMessage interface { type inboundMessage struct { op Op bytesSavedCompression int - fields map[Field]interface{} nodeID ids.NodeID expirationTime time.Time onFinishedHandling func() @@ -45,10 +66,9 @@ func (inMsg *inboundMessage) Op() Op { return inMsg.op } // compression. That is, the number of bytes we did not receive over the // network due to the message being compressed. 0 for messages that were not // compressed. -func (inMsg *inboundMessage) BytesSavedCompression() int { return inMsg.bytesSavedCompression } - -// Field returns the value of the specified field in this message -func (inMsg *inboundMessage) Get(field Field) interface{} { return inMsg.fields[field] } +func (inMsg *inboundMessage) BytesSavedCompression() int { + return inMsg.bytesSavedCompression +} // NodeID returns the node that the msg was sent by. func (inMsg *inboundMessage) NodeID() ids.NodeID { return inMsg.nodeID } @@ -65,7 +85,22 @@ func (inMsg *inboundMessage) OnFinishedHandling() { } } -func (inMsg *inboundMessage) String() string { +type inboundMessageWithPacker struct { + inboundMessage + + fields map[Field]interface{} +} + +// Field returns the value of the specified field in this message +func (inMsg *inboundMessageWithPacker) Get(field Field) (interface{}, error) { + value, ok := inMsg.fields[field] + if !ok { + return nil, fmt.Errorf("%w: %s", errMissingField, field) + } + return value, nil +} + +func (inMsg *inboundMessageWithPacker) String() string { sb := strings.Builder{} sb.WriteString(fmt.Sprintf("(Op: %s, NodeID: %s", inMsg.op, inMsg.nodeID)) if requestIDIntf, exists := inMsg.fields[RequestID]; exists { @@ -77,7 +112,7 @@ func (inMsg *inboundMessage) String() string { switch inMsg.op { case GetAccepted, Accepted, Chits, AcceptedFrontier: sb.WriteString(fmt.Sprintf(", NumContainerIDs: %d)", len(inMsg.fields[ContainerIDs].([][]byte)))) - case Get, GetAncestors, Put, PushQuery, PullQuery: + case Get, GetAncestors, PullQuery: sb.WriteString(fmt.Sprintf(", ContainerID: 0x%x)", inMsg.fields[ContainerID].([]byte))) case Ancestors: sb.WriteString(fmt.Sprintf(", NumContainers: %d)", len(inMsg.fields[MultiContainerBytes].([][]byte)))) @@ -92,6 +127,316 @@ func (inMsg *inboundMessage) String() string { return sb.String() } +type inboundMessageWithProto struct { + inboundMessage + + msg *p2ppb.Message +} + +func (inMsg *inboundMessageWithProto) String() string { + return inMsg.msg.String() +} + +func (inMsg *inboundMessageWithProto) Get(field Field) (interface{}, error) { + return getField(inMsg.msg, field) +} + +// TODO: once protobuf-based p2p messaging is fully activated, +// move the semantic checks out of this package +func getField(m *p2ppb.Message, field Field) (interface{}, error) { + switch m.GetMessage().(type) { + case *p2ppb.Message_Pong: + msg := m.GetPong() + if field == Uptime { + // the original packer-based pong base uses uint8 + return uint8(msg.UptimePct), nil + } + + case *p2ppb.Message_Version: + msg := m.GetVersion() + switch field { + case NetworkID: + return msg.NetworkId, nil + case MyTime: + return msg.MyTime, nil + case IP: + // "net.IP" type in Golang is 16-byte + // regardless of whether it's IPV4 or 6 (see net.IPv6len) + // however, proto message does not enforce the length + // so we need to verify here + // TODO: once we complete the migration + // move this semantic verification outside of this package + if len(msg.IpAddr) != net.IPv6len { + return nil, fmt.Errorf( + "%w: invalid IP address length %d in version message", + errInvalidIPAddrLen, + len(msg.IpAddr), + ) + } + return ips.IPPort{ + IP: net.IP(msg.IpAddr), + Port: uint16(msg.IpPort), + }, nil + case VersionStr: + return msg.MyVersion, nil + case VersionTime: + return msg.MyVersionTime, nil + case SigBytes: + return msg.Sig, nil + case TrackedSubnets: + return msg.TrackedSubnets, nil + } + + case *p2ppb.Message_PeerList: + msg := m.GetPeerList() + if field == Peers { + peers := make([]ips.ClaimedIPPort, len(msg.GetClaimedIpPorts())) + for i, p := range msg.GetClaimedIpPorts() { + tlsCert, err := x509.ParseCertificate(p.X509Certificate) + if err != nil { + // this certificate is different than the certificate received + // during the TLS handshake (and so this error can occur) + return nil, fmt.Errorf( + "%w: failed to parse peer certificate for peer_list message (%v)", + errInvalidCert, + err, + ) + } + // TODO: once we complete the migration + // move this semantic verification outside of this package + if len(p.IpAddr) != net.IPv6len { + return nil, fmt.Errorf( + "%w: invalid IP address length %d in peer_list message", + errInvalidIPAddrLen, + len(p.IpAddr), + ) + } + peers[i] = ips.ClaimedIPPort{ + Cert: tlsCert, + IPPort: ips.IPPort{ + IP: net.IP(p.IpAddr), + Port: uint16(p.IpPort), + }, + Timestamp: p.Timestamp, + Signature: p.Signature, + } + } + return peers, nil + } + + case *p2ppb.Message_GetStateSummaryFrontier: + msg := m.GetGetStateSummaryFrontier() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + } + + case *p2ppb.Message_StateSummaryFrontier_: + msg := m.GetStateSummaryFrontier_() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case SummaryBytes: + return msg.Summary, nil + } + + case *p2ppb.Message_GetAcceptedStateSummary: + msg := m.GetGetAcceptedStateSummary() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + case SummaryHeights: + return msg.Heights, nil + } + + case *p2ppb.Message_AcceptedStateSummary_: + msg := m.GetAcceptedStateSummary_() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case SummaryIDs: + return msg.SummaryIds, nil + } + + case *p2ppb.Message_GetAcceptedFrontier: + msg := m.GetGetAcceptedFrontier() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + } + + case *p2ppb.Message_AcceptedFrontier_: + msg := m.GetAcceptedFrontier_() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case ContainerIDs: + return msg.ContainerIds, nil + } + + case *p2ppb.Message_GetAccepted: + msg := m.GetGetAccepted() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + case ContainerIDs: + return msg.ContainerIds, nil + } + + case *p2ppb.Message_Accepted_: + msg := m.GetAccepted_() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case ContainerIDs: + return msg.ContainerIds, nil + } + + case *p2ppb.Message_GetAncestors: + msg := m.GetGetAncestors() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + case ContainerID: + return msg.ContainerId, nil + } + + case *p2ppb.Message_Ancestors_: + msg := m.GetAncestors_() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case MultiContainerBytes: + return msg.Containers, nil + } + + case *p2ppb.Message_Get: + msg := m.GetGet() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + case ContainerID: + return msg.ContainerId, nil + } + + case *p2ppb.Message_Put: + msg := m.GetPut() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case ContainerBytes: + return msg.Container, nil + } + + case *p2ppb.Message_PushQuery: + msg := m.GetPushQuery() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + case ContainerBytes: + return msg.Container, nil + } + + case *p2ppb.Message_PullQuery: + msg := m.GetPullQuery() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + case ContainerID: + return msg.ContainerId, nil + } + + case *p2ppb.Message_Chits: + msg := m.GetChits() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case ContainerIDs: + return msg.ContainerIds, nil + } + + case *p2ppb.Message_AppRequest: + msg := m.GetAppRequest() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case Deadline: + return msg.Deadline, nil + case AppBytes: + return msg.AppBytes, nil + } + + case *p2ppb.Message_AppResponse: + msg := m.GetAppResponse() + switch field { + case ChainID: + return msg.ChainId, nil + case RequestID: + return msg.RequestId, nil + case AppBytes: + return msg.AppBytes, nil + } + + case *p2ppb.Message_AppGossip: + msg := m.GetAppGossip() + switch field { + case ChainID: + return msg.ChainId, nil + case AppBytes: + return msg.AppBytes, nil + } + } + return nil, fmt.Errorf("%w: %s", errMissingField, field) +} + // OutboundMessage represents a set of fields for an outbound message that can // be serialized into a byte stream type OutboundMessage interface { @@ -102,17 +447,15 @@ type OutboundMessage interface { AddRef() DecRef() + + IsProto() bool } type outboundMessage struct { + op Op bytes []byte bytesSavedCompression int - op Op bypassThrottling bool - - refLock sync.Mutex - refs int - c *codec } // Op returns the value of the specified operation in this message @@ -125,9 +468,22 @@ func (outMsg *outboundMessage) Bytes() []byte { return outMsg.bytes } // compression. That is, the number of bytes we did not send over the // network due to the message being compressed. 0 for messages that were not // compressed. -func (outMsg *outboundMessage) BytesSavedCompression() int { return outMsg.bytesSavedCompression } +func (outMsg *outboundMessage) BytesSavedCompression() int { + return outMsg.bytesSavedCompression +} + +// BypassThrottling when attempting to send this message +func (outMsg *outboundMessage) BypassThrottling() bool { return outMsg.bypassThrottling } + +type outboundMessageWithPacker struct { + outboundMessage + + refLock sync.Mutex + refs int + c *codec +} -func (outMsg *outboundMessage) AddRef() { +func (outMsg *outboundMessageWithPacker) AddRef() { outMsg.refLock.Lock() defer outMsg.refLock.Unlock() @@ -136,7 +492,7 @@ func (outMsg *outboundMessage) AddRef() { // Once the reference count of this message goes to 0, the byte slice should not // be inspected. -func (outMsg *outboundMessage) DecRef() { +func (outMsg *outboundMessageWithPacker) DecRef() { outMsg.refLock.Lock() defer outMsg.refLock.Unlock() @@ -146,27 +502,244 @@ func (outMsg *outboundMessage) DecRef() { } } -// BypassThrottling when attempting to send this message -func (outMsg *outboundMessage) BypassThrottling() bool { return outMsg.bypassThrottling } +func (outMsg *outboundMessageWithPacker) IsProto() bool { return false } + +type outboundMessageWithProto struct { + outboundMessage + + msg *p2ppb.Message +} + +func (outMsg *outboundMessageWithProto) AddRef() {} +func (outMsg *outboundMessageWithProto) DecRef() {} +func (outMsg *outboundMessageWithProto) IsProto() bool { return true } + +// TODO: add other compression algorithms with extended interface +type msgBuilderProtobuf struct { + gzipCompressor compression.Compressor + clock mockable.Clock + + compressTimeMetrics map[Op]metric.Averager + decompressTimeMetrics map[Op]metric.Averager + + maxMessageTimeout time.Duration +} + +// NOTE: the metrics registration paths are the same as "NewCodecWithMemoryPool"! +// To avoid conflicts, use the different namespace if created at the same time. +func newMsgBuilderProtobuf(namespace string, metrics prometheus.Registerer, maxMessageSize int64, maxMessageTimeout time.Duration) (*msgBuilderProtobuf, error) { + cpr, err := compression.NewGzipCompressor(maxMessageSize) + if err != nil { + return nil, err + } + + mb := &msgBuilderProtobuf{ + gzipCompressor: cpr, + + compressTimeMetrics: make(map[Op]metric.Averager, len(ExternalOps)), + decompressTimeMetrics: make(map[Op]metric.Averager, len(ExternalOps)), + + maxMessageTimeout: maxMessageTimeout, + } + + errs := wrappers.Errs{} + for _, op := range ExternalOps { + if !op.Compressible() { + continue + } + + mb.compressTimeMetrics[op] = metric.NewAveragerWithErrs( + namespace, + fmt.Sprintf("%s_compress_time", op), + fmt.Sprintf("time (in ns) to compress %s messages", op), + metrics, + &errs, + ) + mb.decompressTimeMetrics[op] = metric.NewAveragerWithErrs( + namespace, + fmt.Sprintf("%s_decompress_time", op), + fmt.Sprintf("time (in ns) to decompress %s messages", op), + metrics, + &errs, + ) + } + return mb, errs.Err +} + +// NOTE THAT the passed message must be verified beforehand. +// NOTE THAT the passed message will be modified if compression is enabled. +// TODO: find a way to not in-place modify the message +func (mb *msgBuilderProtobuf) marshal(m *p2ppb.Message, gzipCompress bool) ([]byte, int, time.Duration, error) { + uncompressedMsgBytes, err := proto.Marshal(m) + if err != nil { + return nil, 0, 0, err + } + + if !gzipCompress { + return uncompressedMsgBytes, 0, 0, nil + } + + // If compression is enabled, we marshal twice: + // 1. the original message + // 2. the message with compressed bytes + // + // This recursive packing allows us to avoid an extra compression on/off + // field in the message. + startTime := time.Now() + compressedBytes, err := mb.gzipCompressor.Compress(uncompressedMsgBytes) + if err != nil { + return nil, 0, 0, err + } + compressTook := time.Since(startTime) + + // Original message can be discarded for the compressed message. + m.Message = &p2ppb.Message_CompressedGzip{ + CompressedGzip: compressedBytes, + } + compressedMsgBytes, err := proto.Marshal(m) + if err != nil { + return nil, 0, 0, err + } -type TestMsg struct { - op Op - bytes []byte - bypassThrottling bool + bytesSaved := len(uncompressedMsgBytes) - len(compressedMsgBytes) + return compressedMsgBytes, bytesSaved, compressTook, nil } -func NewTestMsg(op Op, bytes []byte, bypassThrottling bool) *TestMsg { - return &TestMsg{ - op: op, - bytes: bytes, - bypassThrottling: bypassThrottling, +func (mb *msgBuilderProtobuf) unmarshal(b []byte) (Op, *p2ppb.Message, bool, int, time.Duration, error) { + m := new(p2ppb.Message) + if err := proto.Unmarshal(b, m); err != nil { + return 0, nil, false, 0, 0, err + } + + compressed := m.GetCompressedGzip() + if len(compressed) == 0 { + // The message wasn't compressed + op, err := msgToOp(m) + return op, m, false, 0, 0, err + } + + startTime := time.Now() + decompressed, err := mb.gzipCompressor.Decompress(compressed) + if err != nil { + return 0, nil, true, 0, 0, err } + decompressTook := time.Since(startTime) + + if err := proto.Unmarshal(decompressed, m); err != nil { + return 0, nil, true, 0, 0, err + } + + op, err := msgToOp(m) + if err != nil { + return 0, nil, true, 0, 0, err + } + if !op.Compressible() { + return 0, nil, true, 0, 0, errUnexpectedCompressedOp + } + + bytesSavedCompression := len(decompressed) - len(compressed) + return op, m, true, bytesSavedCompression, decompressTook, nil } -func (m *TestMsg) Op() Op { return m.op } -func (*TestMsg) Get(Field) interface{} { return nil } -func (m *TestMsg) Bytes() []byte { return m.bytes } -func (*TestMsg) BytesSavedCompression() int { return 0 } -func (*TestMsg) AddRef() {} -func (*TestMsg) DecRef() {} -func (m *TestMsg) BypassThrottling() bool { return m.bypassThrottling } +func msgToOp(m *p2ppb.Message) (Op, error) { + switch m.GetMessage().(type) { + case *p2ppb.Message_Ping: + return Ping, nil + case *p2ppb.Message_Pong: + return Pong, nil + case *p2ppb.Message_Version: + return Version, nil + case *p2ppb.Message_PeerList: + return PeerList, nil + case *p2ppb.Message_GetStateSummaryFrontier: + return GetStateSummaryFrontier, nil + case *p2ppb.Message_StateSummaryFrontier_: + return StateSummaryFrontier, nil + case *p2ppb.Message_GetAcceptedStateSummary: + return GetAcceptedStateSummary, nil + case *p2ppb.Message_AcceptedStateSummary_: + return AcceptedStateSummary, nil + case *p2ppb.Message_GetAcceptedFrontier: + return GetAcceptedFrontier, nil + case *p2ppb.Message_AcceptedFrontier_: + return AcceptedFrontier, nil + case *p2ppb.Message_GetAccepted: + return GetAccepted, nil + case *p2ppb.Message_Accepted_: + return Accepted, nil + case *p2ppb.Message_GetAncestors: + return GetAncestors, nil + case *p2ppb.Message_Ancestors_: + return Ancestors, nil + case *p2ppb.Message_Get: + return Get, nil + case *p2ppb.Message_Put: + return Put, nil + case *p2ppb.Message_PushQuery: + return PushQuery, nil + case *p2ppb.Message_PullQuery: + return PullQuery, nil + case *p2ppb.Message_Chits: + return Chits, nil + case *p2ppb.Message_AppRequest: + return AppRequest, nil + case *p2ppb.Message_AppResponse: + return AppResponse, nil + case *p2ppb.Message_AppGossip: + return AppGossip, nil + default: + return 0, fmt.Errorf("%w: unknown message %T", errUnknownMessageTypeForOp, m.GetMessage()) + } +} + +// NOTE THAT the passed message will be updated if compression is enabled. +// TODO: find a way to not in-place modify the message +func (mb *msgBuilderProtobuf) createOutbound(op Op, msg *p2ppb.Message, gzipCompress bool, bypassThrottling bool) (*outboundMessageWithProto, error) { + b, saved, compressTook, err := mb.marshal(msg, gzipCompress) + if err != nil { + return nil, err + } + if gzipCompress { + mb.compressTimeMetrics[op].Observe(float64(compressTook)) + } + + return &outboundMessageWithProto{ + outboundMessage: outboundMessage{ + op: op, + bytes: b, + bytesSavedCompression: saved, + bypassThrottling: bypassThrottling, + }, + msg: msg, + }, nil +} + +func (mb *msgBuilderProtobuf) parseInbound(bytes []byte, nodeID ids.NodeID, onFinishedHandling func()) (*inboundMessageWithProto, error) { + op, m, wasCompressed, bytesSavedCompression, decompressTook, err := mb.unmarshal(bytes) + if err != nil { + return nil, err + } + if wasCompressed { + mb.decompressTimeMetrics[op].Observe(float64(decompressTook)) + } + + var expirationTime time.Time + if deadline, err := getField(m, Deadline); err == nil { + deadlineDuration := time.Duration(deadline.(uint64)) + if deadlineDuration > mb.maxMessageTimeout { + deadlineDuration = mb.maxMessageTimeout + } + expirationTime = mb.clock.Time().Add(deadlineDuration) + } + + return &inboundMessageWithProto{ + inboundMessage: inboundMessage{ + op: op, + bytesSavedCompression: bytesSavedCompression, + nodeID: nodeID, + expirationTime: expirationTime, + onFinishedHandling: onFinishedHandling, + }, + msg: m, + }, nil +} diff --git a/avalanchego/message/messages_benchmark_test.go b/avalanchego/message/messages_benchmark_test.go new file mode 100644 index 00000000..346c27da --- /dev/null +++ b/avalanchego/message/messages_benchmark_test.go @@ -0,0 +1,207 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "net" + "os" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/stretchr/testify/require" + + "google.golang.org/protobuf/proto" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/ips" + "github.com/ava-labs/avalanchego/utils/units" + + p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p" +) + +// Benchmarks marshal-ing "Version" message. +// +// e.g., +// +// $ go install -v golang.org/x/tools/cmd/benchcmp@latest +// $ go install -v golang.org/x/perf/cmd/benchstat@latest +// +// $ go test -run=NONE -bench=BenchmarkMarshalVersion > /tmp/cpu.before.txt +// $ USE_PROTO=true go test -run=NONE -bench=BenchmarkMarshalVersion > /tmp/cpu.after.txt +// $ USE_PROTO=true USE_PROTO_BUILDER=true go test -run=NONE -bench=BenchmarkMarshalVersion > /tmp/cpu.after.txt +// $ benchcmp /tmp/cpu.before.txt /tmp/cpu.after.txt +// $ benchstat -alpha 0.03 -geomean /tmp/cpu.before.txt /tmp/cpu.after.txt +// +// $ go test -run=NONE -bench=BenchmarkMarshalVersion -benchmem > /tmp/mem.before.txt +// $ USE_PROTO=true go test -run=NONE -bench=BenchmarkMarshalVersion -benchmem > /tmp/mem.after.txt +// $ USE_PROTO=true USE_PROTO_BUILDER=true go test -run=NONE -bench=BenchmarkMarshalVersion -benchmem > /tmp/mem.after.txt +// $ benchcmp /tmp/mem.before.txt /tmp/mem.after.txt +// $ benchstat -alpha 0.03 -geomean /tmp/mem.before.txt /tmp/mem.after.txt +func BenchmarkMarshalVersion(b *testing.B) { + require := require.New(b) + + b.StopTimer() + + id := ids.GenerateTestID() + + // version message does not require compression + // thus no in-place update for proto test cases + // which makes the benchmarks fairer to proto + // as there's no need to copy the original test message + // for each run + inboundMsg := inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Version, + }, + fields: map[Field]interface{}{ + NetworkID: uint32(1337), + NodeID: uint32(0), + MyTime: uint64(time.Now().Unix()), + IP: ips.IPPort{IP: net.IPv4(1, 2, 3, 4)}, + VersionStr: "v1.2.3", + VersionTime: uint64(time.Now().Unix()), + SigBytes: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{id[:]}, + }, + } + packerCodec, err := NewCodecWithMemoryPool("", prometheus.NewRegistry(), 2*units.MiB, 10*time.Second) + require.NoError(err) + + packerMsg, err := packerCodec.Pack(inboundMsg.op, inboundMsg.fields, inboundMsg.op.Compressible(), false) + require.NoError(err) + + packerMsgN := len(packerMsg.Bytes()) + + protoMsg := p2ppb.Message{ + Message: &p2ppb.Message_Version{ + Version: &p2ppb.Version{ + NetworkId: uint32(1337), + MyTime: uint64(time.Now().Unix()), + IpAddr: []byte(net.IPv4(1, 2, 3, 4).To16()), + IpPort: 0, + MyVersion: "v1.2.3", + MyVersionTime: uint64(time.Now().Unix()), + Sig: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{id[:]}, + }, + }, + } + protoMsgN := proto.Size(&protoMsg) + + useProto := os.Getenv("USE_PROTO") != "" + useProtoBuilder := os.Getenv("USE_PROTO_BUILDER") != "" + + protoCodec, err := newMsgBuilderProtobuf("", prometheus.NewRegistry(), 2*units.MiB, 10*time.Second) + require.NoError(err) + + b.Logf("marshaling packer %d-byte, proto %d-byte (use proto %v, use proto builder %v)", packerMsgN, protoMsgN, useProto, useProtoBuilder) + + b.StartTimer() + for i := 0; i < b.N; i++ { + if !useProto { + // version does not compress message + _, err := packerCodec.Pack(inboundMsg.op, inboundMsg.fields, false, false) + require.NoError(err) + continue + } + + if useProtoBuilder { + _, err = protoCodec.createOutbound(inboundMsg.op, &protoMsg, false, false) + } else { + _, err = proto.Marshal(&protoMsg) + } + require.NoError(err) + } +} + +// Benchmarks unmarshal-ing "Version" message. +// +// e.g., +// +// $ go install -v golang.org/x/tools/cmd/benchcmp@latest +// $ go install -v golang.org/x/perf/cmd/benchstat@latest +// +// $ go test -run=NONE -bench=BenchmarkUnmarshalVersion > /tmp/cpu.before.txt +// $ USE_PROTO=true go test -run=NONE -bench=BenchmarkUnmarshalVersion > /tmp/cpu.after.txt +// $ USE_PROTO=true USE_PROTO_BUILDER=true go test -run=NONE -bench=BenchmarkUnmarshalVersion > /tmp/cpu.after.txt +// $ benchcmp /tmp/cpu.before.txt /tmp/cpu.after.txt +// $ benchstat -alpha 0.03 -geomean /tmp/cpu.before.txt /tmp/cpu.after.txt +// +// $ go test -run=NONE -bench=BenchmarkUnmarshalVersion -benchmem > /tmp/mem.before.txt +// $ USE_PROTO=true go test -run=NONE -bench=BenchmarkUnmarshalVersion -benchmem > /tmp/mem.after.txt +// $ USE_PROTO=true USE_PROTO_BUILDER=true go test -run=NONE -bench=BenchmarkUnmarshalVersion -benchmem > /tmp/mem.after.txt +// $ benchcmp /tmp/mem.before.txt /tmp/mem.after.txt +// $ benchstat -alpha 0.03 -geomean /tmp/mem.before.txt /tmp/mem.after.txt +func BenchmarkUnmarshalVersion(b *testing.B) { + require := require.New(b) + + b.StopTimer() + + id := ids.GenerateTestID() + inboundMsg := inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Version, + }, + fields: map[Field]interface{}{ + NetworkID: uint32(1337), + NodeID: uint32(0), + MyTime: uint64(time.Now().Unix()), + IP: ips.IPPort{IP: net.IPv4(1, 2, 3, 4)}, + VersionStr: "v1.2.3", + VersionTime: uint64(time.Now().Unix()), + SigBytes: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{id[:]}, + }, + } + packerCodec, err := NewCodecWithMemoryPool("", prometheus.NewRegistry(), 2*units.MiB, 10*time.Second) + require.NoError(err) + + protoMsg := p2ppb.Message{ + Message: &p2ppb.Message_Version{ + Version: &p2ppb.Version{ + NetworkId: uint32(1337), + MyTime: uint64(time.Now().Unix()), + IpAddr: []byte(net.IPv4(1, 2, 3, 4).To16()), + IpPort: 0, + MyVersion: "v1.2.3", + MyVersionTime: uint64(time.Now().Unix()), + Sig: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{id[:]}, + }, + }, + } + + rawMsg, err := proto.Marshal(&protoMsg) + require.NoError(err) + + useProto := os.Getenv("USE_PROTO") != "" + if !useProto { + msgInf, err := packerCodec.Pack(inboundMsg.op, inboundMsg.fields, inboundMsg.op.Compressible(), false) + require.NoError(err) + rawMsg = msgInf.Bytes() + } + + useProtoBuilder := os.Getenv("USE_PROTO_BUILDER") != "" + protoCodec, err := newMsgBuilderProtobuf("", prometheus.NewRegistry(), 2*units.MiB, 10*time.Second) + require.NoError(err) + + b.StartTimer() + for i := 0; i < b.N; i++ { + if !useProto { + _, err := packerCodec.Parse(rawMsg, dummyNodeID, dummyOnFinishedHandling) + require.NoError(err) + continue + } + + if useProtoBuilder { + _, err = protoCodec.parseInbound(rawMsg, dummyNodeID, dummyOnFinishedHandling) + } else { + var protoMsg p2ppb.Message + err = proto.Unmarshal(rawMsg, &protoMsg) + } + require.NoError(err) + } +} diff --git a/avalanchego/message/messages_test.go b/avalanchego/message/messages_test.go new file mode 100644 index 00000000..7b0dcd1f --- /dev/null +++ b/avalanchego/message/messages_test.go @@ -0,0 +1,1152 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "bytes" + "fmt" + "net" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/stretchr/testify/require" + + "google.golang.org/protobuf/proto" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/staking" + "github.com/ava-labs/avalanchego/utils/ips" + "github.com/ava-labs/avalanchego/utils/units" + + p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p" +) + +// Ensures the message size with proto not blow up compared to packer. +func TestProtoMarshalSizeVersion(t *testing.T) { + t.Parallel() + require := require.New(t) + + id := ids.GenerateTestID() + inboundMsg := inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Version, + }, + fields: map[Field]interface{}{ + NetworkID: uint32(1337), + NodeID: uint32(0), + MyTime: uint64(time.Now().Unix()), + IP: ips.IPPort{IP: net.IPv4(1, 2, 3, 4)}, + VersionStr: "v1.2.3", + VersionTime: uint64(time.Now().Unix()), + SigBytes: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{id[:]}, + }, + } + packerCodec, err := NewCodecWithMemoryPool( + "", + prometheus.NewRegistry(), + 2*units.MiB, + 10*time.Second, + ) + require.NoError(err) + + packerMsg, err := packerCodec.Pack( + inboundMsg.op, + inboundMsg.fields, + inboundMsg.op.Compressible(), + false, + ) + require.NoError(err) + + packerMsgN := len(packerMsg.Bytes()) + + protoMsg := p2ppb.Message{ + Message: &p2ppb.Message_Version{ + Version: &p2ppb.Version{ + NetworkId: uint32(1337), + MyTime: uint64(time.Now().Unix()), + IpAddr: []byte(net.IPv4(1, 2, 3, 4).To16()), + IpPort: 0, + MyVersion: "v1.2.3", + MyVersionTime: uint64(time.Now().Unix()), + Sig: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{id[:]}, + }, + }, + } + protoMsgN := proto.Size(&protoMsg) + + t.Logf("marshaled; packer %d-byte, proto %d-byte", packerMsgN, protoMsgN) + require.Greater(packerMsgN, protoMsgN) +} + +// Ensures the message size with proto not blow up compared to packer. +func TestProtoMarshalSizeAncestors(t *testing.T) { + t.Parallel() + require := require.New(t) + + id := ids.GenerateTestID() + inboundMsg := inboundMessageWithPacker{ + inboundMessage: inboundMessage{ + op: Ancestors, + }, + fields: map[Field]interface{}{ + ChainID: id[:], + RequestID: uint32(12345), + MultiContainerBytes: [][]byte{ + bytes.Repeat([]byte{0}, 100), + bytes.Repeat([]byte{0}, 100), + bytes.Repeat([]byte{0}, 100), + }, + }, + } + packerCodec, err := NewCodecWithMemoryPool( + "", + prometheus.NewRegistry(), + 2*units.MiB, + 10*time.Second, + ) + require.NoError(err) + + compressible := inboundMsg.op.Compressible() + require.True(compressible) + + packerMsg, err := packerCodec.Pack( + inboundMsg.op, + inboundMsg.fields, + compressible, + false, + ) + require.NoError(err) + + packerMsgN := len(packerMsg.Bytes()) + + protoMsg := p2ppb.Message{ + Message: &p2ppb.Message_Ancestors_{ + Ancestors_: &p2ppb.Ancestors{ + ChainId: id[:], + RequestId: 12345, + Containers: [][]byte{ + bytes.Repeat([]byte{0}, 100), + bytes.Repeat([]byte{0}, 100), + bytes.Repeat([]byte{0}, 100), + }, + }, + }, + } + + mb, err := newMsgBuilderProtobuf( + "test", + prometheus.NewRegistry(), + units.MiB, + 5*time.Second, + ) + require.NoError(err) + + b, _, _, err := mb.marshal(&protoMsg, compressible) + require.NoError(err) + + protoMsgN := len(b) + t.Logf("marshaled; packer %d-byte, proto %d-byte", packerMsgN, protoMsgN) + + require.GreaterOrEqual(packerMsgN, protoMsgN) +} + +func TestNewOutboundInboundMessageWithProto(t *testing.T) { + t.Parallel() + + require := require.New(t) + + mb, err := newMsgBuilderProtobuf( + "test", + prometheus.NewRegistry(), + units.MiB, + 5*time.Second, + ) + require.NoError(err) + + testID := ids.GenerateTestID() + compressibleContainers := [][]byte{ + bytes.Repeat([]byte{0}, 100), + bytes.Repeat([]byte{0}, 32), + bytes.Repeat([]byte{0}, 32), + } + + testCertRaw, testKeyRaw, err := staking.NewCertAndKeyBytes() + require.NoError(err) + + testTLSCert, err := staking.LoadTLSCertFromBytes(testKeyRaw, testCertRaw) + require.NoError(err) + + nowUnix := time.Now().Unix() + + tests := []struct { + desc string + op Op + msg *p2ppb.Message + gzipCompress bool + bypassThrottling bool + bytesSaved bool // if true, outbound message saved bytes must be non-zero + expectedOutboundErr error // expected error for creating outbound message + fields map[Field]interface{} // expected fields from the inbound message + expectedGetFieldErr map[Field]error // expected error for getting the specified field + }{ + { + desc: "valid pong outbound message with no compression", + op: Pong, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Pong{ + Pong: &p2ppb.Pong{ + UptimePct: 1, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + Uptime: uint8(1), + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid ping outbound message with no compression", + op: Ping, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Ping{ + Ping: &p2ppb.Ping{}, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: nil, + expectedGetFieldErr: nil, + }, + { + desc: "valid get_accepted_frontier outbound message with no compression", + op: GetAcceptedFrontier, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_GetAcceptedFrontier{ + GetAcceptedFrontier: &p2ppb.GetAcceptedFrontier{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid accepted_frontier outbound message with no compression", + op: AcceptedFrontier, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AcceptedFrontier_{ + AcceptedFrontier_: &p2ppb.AcceptedFrontier{ + ChainId: testID[:], + RequestId: 1, + ContainerIds: [][]byte{testID[:], testID[:]}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + ContainerIDs: [][]byte{testID[:], testID[:]}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid get_accepted outbound message with no compression", + op: GetAccepted, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_GetAccepted{ + GetAccepted: &p2ppb.GetAccepted{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + ContainerIds: [][]byte{testID[:], testID[:]}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + ContainerIDs: [][]byte{testID[:], testID[:]}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid accepted outbound message with no compression", + op: Accepted, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Accepted_{ + Accepted_: &p2ppb.Accepted{ + ChainId: testID[:], + RequestId: 1, + ContainerIds: [][]byte{testID[:], testID[:]}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + ContainerIDs: [][]byte{testID[:], testID[:]}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid get_ancestors outbound message with no compression", + op: GetAncestors, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_GetAncestors{ + GetAncestors: &p2ppb.GetAncestors{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + ContainerId: testID[:], + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + ContainerID: testID[:], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid ancestor outbound message with no compression", + op: Ancestors, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Ancestors_{ + Ancestors_: &p2ppb.Ancestors{ + ChainId: testID[:], + RequestId: 12345, + Containers: compressibleContainers, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(12345), + MultiContainerBytes: compressibleContainers, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid ancestor outbound message with compression", + op: Ancestors, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Ancestors_{ + Ancestors_: &p2ppb.Ancestors{ + ChainId: testID[:], + RequestId: 12345, + Containers: compressibleContainers, + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(12345), + MultiContainerBytes: compressibleContainers, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid get outbound message with no compression", + op: Get, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Get{ + Get: &p2ppb.Get{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + ContainerId: testID[:], + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + ContainerID: testID[:], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid put outbound message with no compression", + op: Put, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Put{ + Put: &p2ppb.Put{ + ChainId: testID[:], + RequestId: 1, + Container: []byte{0}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + ContainerBytes: []byte{0}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid put outbound message with compression", + op: Put, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Put{ + Put: &p2ppb.Put{ + ChainId: testID[:], + RequestId: 1, + Container: compressibleContainers[0], + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + ContainerBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid push_query outbound message with no compression", + op: PushQuery, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PushQuery{ + PushQuery: &p2ppb.PushQuery{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + Container: []byte{0}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + ContainerBytes: []byte{0}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid push_query outbound message with compression", + op: PushQuery, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PushQuery{ + PushQuery: &p2ppb.PushQuery{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + Container: compressibleContainers[0], + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + ContainerBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid pull_query outbound message with no compression", + op: PullQuery, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PullQuery{ + PullQuery: &p2ppb.PullQuery{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + ContainerId: testID[:], + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + ContainerID: testID[:], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid chits outbound message with no compression", + op: Chits, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Chits{ + Chits: &p2ppb.Chits{ + ChainId: testID[:], + RequestId: 1, + ContainerIds: [][]byte{testID[:], testID[:]}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + ContainerIDs: [][]byte{testID[:], testID[:]}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid peer_list outbound message with no compression", + op: PeerList, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PeerList{ + PeerList: &p2ppb.PeerList{ + ClaimedIpPorts: []*p2ppb.ClaimedIpPort{ + { + X509Certificate: testTLSCert.Certificate[0], + IpAddr: []byte(net.IPv4zero), + IpPort: 10, + Timestamp: 1, + Signature: []byte{0}, + }, + }, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + Peers: []ips.ClaimedIPPort{ + { + Cert: testTLSCert.Leaf, + IPPort: ips.IPPort{IP: net.IPv4zero, Port: uint16(10)}, + Timestamp: uint64(1), + Signature: []byte{0}, + }, + }, + }, + expectedGetFieldErr: nil, + }, + { + desc: "invalid peer_list inbound message with invalid cert", + op: PeerList, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PeerList{ + PeerList: &p2ppb.PeerList{ + ClaimedIpPorts: []*p2ppb.ClaimedIpPort{ + { + X509Certificate: []byte{0}, + IpAddr: []byte(net.IPv4zero[4:]), + IpPort: 10, + Timestamp: 1, + Signature: []byte{0}, + }, + }, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + Peers: nil, + }, + expectedGetFieldErr: map[Field]error{Peers: errInvalidCert}, + }, + { + desc: "invalid peer_list inbound message with invalid ip", + op: PeerList, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PeerList{ + PeerList: &p2ppb.PeerList{ + ClaimedIpPorts: []*p2ppb.ClaimedIpPort{ + { + X509Certificate: testTLSCert.Certificate[0], + IpAddr: []byte(net.IPv4zero[4:]), + IpPort: 10, + Timestamp: 1, + Signature: []byte{0}, + }, + }, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + Peers: nil, + }, + expectedGetFieldErr: map[Field]error{Peers: errInvalidIPAddrLen}, + }, + { + desc: "valid peer_list outbound message with compression", + op: PeerList, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PeerList{ + PeerList: &p2ppb.PeerList{ + ClaimedIpPorts: []*p2ppb.ClaimedIpPort{ + { + X509Certificate: testTLSCert.Certificate[0], + IpAddr: []byte(net.IPv6zero), + IpPort: 9651, + Timestamp: uint64(nowUnix), + Signature: compressibleContainers[0], + }, + }, + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + Peers: []ips.ClaimedIPPort{ + { + Cert: testTLSCert.Leaf, + IPPort: ips.IPPort{IP: net.IPv6zero, Port: uint16(9651)}, + Timestamp: uint64(nowUnix), + Signature: compressibleContainers[0], + }, + }, + }, + expectedGetFieldErr: nil, + }, + { + desc: "invalid peer_list outbound message with compression and invalid cert", + op: PeerList, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PeerList{ + PeerList: &p2ppb.PeerList{ + ClaimedIpPorts: []*p2ppb.ClaimedIpPort{ + { + X509Certificate: testTLSCert.Certificate[0][10:], + IpAddr: []byte(net.IPv6zero), + IpPort: 9651, + Timestamp: uint64(nowUnix), + Signature: compressibleContainers[0], + }, + }, + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + Peers: nil, + }, + expectedGetFieldErr: map[Field]error{Peers: errInvalidCert}, + }, + { + desc: "invalid peer_list outbound message with compression and invalid ip", + op: PeerList, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_PeerList{ + PeerList: &p2ppb.PeerList{ + ClaimedIpPorts: []*p2ppb.ClaimedIpPort{ + { + X509Certificate: testTLSCert.Certificate[0], + IpAddr: []byte(net.IPv6zero[:5]), + IpPort: 9651, + Timestamp: uint64(nowUnix), + Signature: compressibleContainers[0], + }, + }, + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + Peers: nil, + }, + expectedGetFieldErr: map[Field]error{Peers: errInvalidIPAddrLen}, + }, + { + desc: "valid version outbound message with no compression", + op: Version, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Version{ + Version: &p2ppb.Version{ + NetworkId: uint32(1337), + MyTime: uint64(nowUnix), + IpAddr: []byte(net.IPv6zero), + IpPort: 9651, + MyVersion: "v1.2.3", + MyVersionTime: uint64(nowUnix), + Sig: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{testID[:]}, + }, + }, + }, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + NetworkID: uint32(1337), + MyTime: uint64(nowUnix), + IP: ips.IPPort{IP: net.IPv6zero, Port: uint16(9651)}, + VersionStr: "v1.2.3", + VersionTime: uint64(nowUnix), + SigBytes: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{testID[:]}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "invalid version inbound message with invalid ip", + op: Version, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_Version{ + Version: &p2ppb.Version{ + NetworkId: uint32(1337), + MyTime: uint64(nowUnix), + IpAddr: []byte(net.IPv6zero[1:]), + IpPort: 9651, + MyVersion: "v1.2.3", + MyVersionTime: uint64(nowUnix), + Sig: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{testID[:]}, + }, + }, + }, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + NetworkID: uint32(1337), + MyTime: uint64(nowUnix), + IP: nil, + VersionStr: "v1.2.3", + VersionTime: uint64(nowUnix), + SigBytes: []byte{'y', 'e', 'e', 't'}, + TrackedSubnets: [][]byte{testID[:]}, + }, + expectedGetFieldErr: map[Field]error{IP: errInvalidIPAddrLen}, + }, + { + desc: "valid app_request outbound message with no compression", + op: AppRequest, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AppRequest{ + AppRequest: &p2ppb.AppRequest{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + AppBytes: compressibleContainers[0], + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + AppBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid app_request outbound message with compression", + op: AppRequest, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AppRequest{ + AppRequest: &p2ppb.AppRequest{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + AppBytes: compressibleContainers[0], + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + AppBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid app_response outbound message with no compression", + op: AppResponse, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AppResponse{ + AppResponse: &p2ppb.AppResponse{ + ChainId: testID[:], + RequestId: 1, + AppBytes: compressibleContainers[0], + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + AppBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid app_response outbound message with compression", + op: AppResponse, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AppResponse{ + AppResponse: &p2ppb.AppResponse{ + ChainId: testID[:], + RequestId: 1, + AppBytes: compressibleContainers[0], + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + AppBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid app_gossip outbound message with no compression", + op: AppGossip, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AppGossip{ + AppGossip: &p2ppb.AppGossip{ + ChainId: testID[:], + AppBytes: compressibleContainers[0], + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + AppBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid app_gossip outbound message with compression", + op: AppGossip, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AppGossip{ + AppGossip: &p2ppb.AppGossip{ + ChainId: testID[:], + AppBytes: compressibleContainers[0], + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + AppBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid get_state_summary_frontier outbound message with no compression", + op: GetStateSummaryFrontier, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_GetStateSummaryFrontier{ + GetStateSummaryFrontier: &p2ppb.GetStateSummaryFrontier{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid state_summary_frontier outbound message with no compression", + op: StateSummaryFrontier, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_StateSummaryFrontier_{ + StateSummaryFrontier_: &p2ppb.StateSummaryFrontier{ + ChainId: testID[:], + RequestId: 1, + Summary: []byte{0}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + SummaryBytes: []byte{0}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid state_summary_frontier outbound message with compression", + op: StateSummaryFrontier, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_StateSummaryFrontier_{ + StateSummaryFrontier_: &p2ppb.StateSummaryFrontier{ + ChainId: testID[:], + RequestId: 1, + Summary: compressibleContainers[0], + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + SummaryBytes: compressibleContainers[0], + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid get_accepted_state_summary_frontier outbound message with no compression", + op: GetAcceptedStateSummary, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_GetAcceptedStateSummary{ + GetAcceptedStateSummary: &p2ppb.GetAcceptedStateSummary{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + Heights: []uint64{0}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + SummaryHeights: []uint64{0}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid get_accepted_state_summary_frontier outbound message with compression", + op: GetAcceptedStateSummary, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_GetAcceptedStateSummary{ + GetAcceptedStateSummary: &p2ppb.GetAcceptedStateSummary{ + ChainId: testID[:], + RequestId: 1, + Deadline: 1, + Heights: []uint64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + Deadline: uint64(1), + SummaryHeights: []uint64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid accepted_state_summary_frontier outbound message with no compression", + op: AcceptedStateSummary, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AcceptedStateSummary_{ + AcceptedStateSummary_: &p2ppb.AcceptedStateSummary{ + ChainId: testID[:], + RequestId: 1, + SummaryIds: [][]byte{testID[:], testID[:]}, + }, + }, + }, + gzipCompress: false, + bypassThrottling: true, + bytesSaved: false, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + SummaryIDs: [][]byte{testID[:], testID[:]}, + }, + expectedGetFieldErr: nil, + }, + { + desc: "valid accepted_state_summary_frontier outbound message with compression", + op: AcceptedStateSummary, + msg: &p2ppb.Message{ + Message: &p2ppb.Message_AcceptedStateSummary_{ + AcceptedStateSummary_: &p2ppb.AcceptedStateSummary{ + ChainId: testID[:], + RequestId: 1, + SummaryIds: [][]byte{testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:]}, + }, + }, + }, + gzipCompress: true, + bypassThrottling: true, + bytesSaved: true, + expectedOutboundErr: nil, + fields: map[Field]interface{}{ + ChainID: testID[:], + RequestID: uint32(1), + SummaryIDs: [][]byte{testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:]}, + }, + }, + } + + for _, tv := range tests { + require.True(t.Run(tv.desc, func(t2 *testing.T) { + // copy before we in-place update via marshal + oldProtoMsgS := tv.msg.String() + + encodedMsg, err := mb.createOutbound(tv.op, tv.msg, tv.gzipCompress, tv.bypassThrottling) + require.ErrorIs(err, tv.expectedOutboundErr, fmt.Errorf("unexpected error %v (%T)", err, err)) + if tv.expectedOutboundErr != nil { + return + } + + require.Equal(encodedMsg.BypassThrottling(), tv.bypassThrottling) + + bytesSaved := encodedMsg.BytesSavedCompression() + if bytesSaved > 0 { + t2.Logf("saved %d bytes", bytesSaved) + } + require.Equal(tv.bytesSaved, bytesSaved > 0) + + if (bytesSaved > 0) != tv.bytesSaved { + // if bytes saved expected via compression, + // the outbound message BytesSavedCompression should return >bytesSaved + t.Fatalf("unexpected BytesSavedCompression>0 (%d), expected bytes saved %v", bytesSaved, tv.bytesSaved) + } + + parsedMsg, err := mb.parseInbound(encodedMsg.Bytes(), ids.EmptyNodeID, func() {}) + require.NoError(err) + + // before/after compression, the message should be the same + require.Equal(parsedMsg.msg.String(), oldProtoMsgS) + + for field, v1 := range tv.fields { + v2, err := getField(parsedMsg.msg, field) + + // expects "getField" error + if expectedGetFieldErr, ok := tv.expectedGetFieldErr[field]; ok { + require.ErrorIs(err, expectedGetFieldErr) + continue + } + + require.NoError(err) + require.Equal(v1, v2) + } + })) + } +} diff --git a/avalanchego/message/ops.go b/avalanchego/message/ops.go index 412c4f25..4ded5283 100644 --- a/avalanchego/message/ops.go +++ b/avalanchego/message/ops.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -47,8 +47,6 @@ const ( StateSummaryFrontier GetAcceptedStateSummary AcceptedStateSummary - // linearize dag - ChitsV2 // Internal messages (External messages should be added above these): GetAcceptedFrontierFailed @@ -92,7 +90,6 @@ var ( Ancestors, Put, Chits, - ChitsV2, AppResponse, StateSummaryFrontier, AcceptedStateSummary, @@ -137,7 +134,6 @@ var ( PushQuery, PullQuery, Chits, - ChitsV2, GetAcceptedFrontierFailed, GetAcceptedFailed, GetAncestorsFailed, @@ -179,23 +175,19 @@ var ( Ancestors: GetAncestorsFailed, Put: GetFailed, Chits: QueryFailed, - ChitsV2: QueryFailed, AppResponse: AppRequestFailed, StateSummaryFrontier: GetStateSummaryFrontierFailed, AcceptedStateSummary: GetAcceptedStateSummaryFailed, } FailedToResponseOps = map[Op]Op{ - GetAcceptedFrontierFailed: AcceptedFrontier, - GetAcceptedFailed: Accepted, - GetAncestorsFailed: Ancestors, - GetFailed: Put, - - // "ChitV2" response failure also uses "Chits" - QueryFailed: Chits, - - AppRequestFailed: AppResponse, GetStateSummaryFrontierFailed: StateSummaryFrontier, GetAcceptedStateSummaryFailed: AcceptedStateSummary, + GetAcceptedFrontierFailed: AcceptedFrontier, + GetAcceptedFailed: Accepted, + GetAncestorsFailed: Ancestors, + GetFailed: Put, + QueryFailed: Chits, + AppRequestFailed: AppResponse, } UnrequestedOps = map[Op]struct{}{ GetAcceptedFrontier: {}, @@ -232,16 +224,6 @@ var ( PullQuery: {ChainID, RequestID, Deadline, ContainerID}, Chits: {ChainID, RequestID, ContainerIDs}, - // ChitV2 is used for transition from DAG to linear chain - // First "ContainerIDs" field represents the votes from the existing DAG - // Second "ContainerID" field represents the vote from newly instantiated snowman chain - // The message sender should populate both, and the message handler should choose "one" - // depending on the consensus engine it's running at the time. - // - // TODO: define a new chit message v3 with container ID as a single value - // once DAG is linearized - ChitsV2: {ChainID, RequestID, ContainerIDs, ContainerID}, - // Application level: AppRequest: {ChainID, RequestID, Deadline, AppBytes}, AppResponse: {ChainID, RequestID, AppBytes}, @@ -297,8 +279,6 @@ func (op Op) String() string { return "pull_query" case Chits: return "chits" - case ChitsV2: - return "chits_v2" case AppRequest: return "app_request" case AppResponse: diff --git a/avalanchego/message/outbound_msg_builder.go b/avalanchego/message/outbound_msg_builder.go index c3ccc5d3..8a6c3f5f 100644 --- a/avalanchego/message/outbound_msg_builder.go +++ b/avalanchego/message/outbound_msg_builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message @@ -10,7 +10,7 @@ import ( "github.com/ava-labs/avalanchego/utils/ips" ) -var _ OutboundMsgBuilder = &outMsgBuilder{} +var _ OutboundMsgBuilder = &outMsgBuilderWithPacker{} // OutboundMsgBuilder builds outbound messages. Outbound messages are returned // with a reference count of 1. Once the reference count hits 0, the message @@ -108,7 +108,6 @@ type OutboundMsgBuilder interface { Put( chainID ids.ID, requestID uint32, - containerID ids.ID, container []byte, ) (OutboundMessage, error) @@ -116,7 +115,6 @@ type OutboundMsgBuilder interface { chainID ids.ID, requestID uint32, deadline time.Duration, - containerID ids.ID, container []byte, ) (OutboundMessage, error) @@ -133,13 +131,6 @@ type OutboundMsgBuilder interface { containerIDs []ids.ID, ) (OutboundMessage, error) - ChitsV2( - chainID ids.ID, - requestID uint32, - containerIDs []ids.ID, - containerID ids.ID, - ) (OutboundMessage, error) - AppRequest( chainID ids.ID, requestID uint32, @@ -159,19 +150,19 @@ type OutboundMsgBuilder interface { ) (OutboundMessage, error) } -type outMsgBuilder struct { +type outMsgBuilderWithPacker struct { c Codec compress bool } -func NewOutboundBuilder(c Codec, enableCompression bool) OutboundMsgBuilder { - return &outMsgBuilder{ +func NewOutboundBuilderWithPacker(c Codec, enableCompression bool) OutboundMsgBuilder { + return &outMsgBuilderWithPacker{ c: c, compress: enableCompression, } } -func (b *outMsgBuilder) Version( +func (b *outMsgBuilderWithPacker) Version( networkID uint32, myTime uint64, ip ips.IPPort, @@ -197,43 +188,43 @@ func (b *outMsgBuilder) Version( SigBytes: sig, TrackedSubnets: subnetIDBytes, }, - Version.Compressible(), // Version Messages can't be compressed + b.compress && Version.Compressible(), true, ) } -func (b *outMsgBuilder) PeerList(peers []ips.ClaimedIPPort, bypassThrottling bool) (OutboundMessage, error) { +func (b *outMsgBuilderWithPacker) PeerList(peers []ips.ClaimedIPPort, bypassThrottling bool) (OutboundMessage, error) { return b.c.Pack( PeerList, map[Field]interface{}{ Peers: peers, }, - b.compress && PeerList.Compressible(), // PeerList messages may be compressed + b.compress && PeerList.Compressible(), bypassThrottling, ) } -func (b *outMsgBuilder) Ping() (OutboundMessage, error) { +func (b *outMsgBuilderWithPacker) Ping() (OutboundMessage, error) { return b.c.Pack( Ping, nil, - Ping.Compressible(), // Ping messages can't be compressed + b.compress && Ping.Compressible(), false, ) } -func (b *outMsgBuilder) Pong(uptimePercentage uint8) (OutboundMessage, error) { +func (b *outMsgBuilderWithPacker) Pong(uptimePercentage uint8) (OutboundMessage, error) { return b.c.Pack( Pong, map[Field]interface{}{ Uptime: uptimePercentage, }, - Pong.Compressible(), // Pong messages can't be compressed + b.compress && Pong.Compressible(), false, ) } -func (b *outMsgBuilder) GetStateSummaryFrontier( +func (b *outMsgBuilderWithPacker) GetStateSummaryFrontier( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -245,12 +236,12 @@ func (b *outMsgBuilder) GetStateSummaryFrontier( RequestID: requestID, Deadline: uint64(deadline), }, - GetStateSummaryFrontier.Compressible(), // GetStateSummaryFrontier messages can't be compressed + b.compress && GetStateSummaryFrontier.Compressible(), false, ) } -func (b *outMsgBuilder) StateSummaryFrontier( +func (b *outMsgBuilderWithPacker) StateSummaryFrontier( chainID ids.ID, requestID uint32, summary []byte, @@ -262,12 +253,12 @@ func (b *outMsgBuilder) StateSummaryFrontier( RequestID: requestID, SummaryBytes: summary, }, - b.compress && StateSummaryFrontier.Compressible(), // StateSummaryFrontier messages may be compressed + b.compress && StateSummaryFrontier.Compressible(), false, ) } -func (b *outMsgBuilder) GetAcceptedStateSummary( +func (b *outMsgBuilderWithPacker) GetAcceptedStateSummary( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -281,12 +272,12 @@ func (b *outMsgBuilder) GetAcceptedStateSummary( Deadline: uint64(deadline), SummaryHeights: heights, }, - b.compress && GetAcceptedStateSummary.Compressible(), // GetAcceptedStateSummary messages may be compressed + b.compress && GetAcceptedStateSummary.Compressible(), false, ) } -func (b *outMsgBuilder) AcceptedStateSummary( +func (b *outMsgBuilderWithPacker) AcceptedStateSummary( chainID ids.ID, requestID uint32, summaryIDs []ids.ID, @@ -300,12 +291,12 @@ func (b *outMsgBuilder) AcceptedStateSummary( RequestID: requestID, SummaryIDs: summaryIDBytes, }, - b.compress && AcceptedStateSummary.Compressible(), // AcceptedStateSummary messages may be compressed + b.compress && AcceptedStateSummary.Compressible(), false, ) } -func (b *outMsgBuilder) GetAcceptedFrontier( +func (b *outMsgBuilderWithPacker) GetAcceptedFrontier( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -317,12 +308,12 @@ func (b *outMsgBuilder) GetAcceptedFrontier( RequestID: requestID, Deadline: uint64(deadline), }, - GetAcceptedFrontier.Compressible(), // GetAcceptedFrontier messages can't be compressed + b.compress && GetAcceptedFrontier.Compressible(), false, ) } -func (b *outMsgBuilder) AcceptedFrontier( +func (b *outMsgBuilderWithPacker) AcceptedFrontier( chainID ids.ID, requestID uint32, containerIDs []ids.ID, @@ -336,12 +327,12 @@ func (b *outMsgBuilder) AcceptedFrontier( RequestID: requestID, ContainerIDs: containerIDBytes, }, - AcceptedFrontier.Compressible(), // AcceptedFrontier messages can't be compressed + b.compress && AcceptedFrontier.Compressible(), false, ) } -func (b *outMsgBuilder) GetAccepted( +func (b *outMsgBuilderWithPacker) GetAccepted( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -357,12 +348,12 @@ func (b *outMsgBuilder) GetAccepted( Deadline: uint64(deadline), ContainerIDs: containerIDBytes, }, - GetAccepted.Compressible(), // GetAccepted messages can't be compressed + b.compress && GetAccepted.Compressible(), false, ) } -func (b *outMsgBuilder) Accepted( +func (b *outMsgBuilderWithPacker) Accepted( chainID ids.ID, requestID uint32, containerIDs []ids.ID, @@ -376,12 +367,12 @@ func (b *outMsgBuilder) Accepted( RequestID: requestID, ContainerIDs: containerIDBytes, }, - Accepted.Compressible(), // Accepted messages can't be compressed + b.compress && Accepted.Compressible(), false, ) } -func (b *outMsgBuilder) GetAncestors( +func (b *outMsgBuilderWithPacker) GetAncestors( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -395,12 +386,12 @@ func (b *outMsgBuilder) GetAncestors( Deadline: uint64(deadline), ContainerID: containerID[:], }, - GetAncestors.Compressible(), // GetAncestors messages can't be compressed + b.compress && GetAncestors.Compressible(), false, ) } -func (b *outMsgBuilder) Ancestors( +func (b *outMsgBuilderWithPacker) Ancestors( chainID ids.ID, requestID uint32, containers [][]byte, @@ -412,12 +403,12 @@ func (b *outMsgBuilder) Ancestors( RequestID: requestID, MultiContainerBytes: containers, }, - b.compress && Ancestors.Compressible(), // Ancestors messages may be compressed + b.compress && Ancestors.Compressible(), false, ) } -func (b *outMsgBuilder) Get( +func (b *outMsgBuilderWithPacker) Get( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -431,15 +422,14 @@ func (b *outMsgBuilder) Get( Deadline: uint64(deadline), ContainerID: containerID[:], }, - Get.Compressible(), // Get messages can't be compressed + b.compress && Get.Compressible(), false, ) } -func (b *outMsgBuilder) Put( +func (b *outMsgBuilderWithPacker) Put( chainID ids.ID, requestID uint32, - containerID ids.ID, container []byte, ) (OutboundMessage, error) { return b.c.Pack( @@ -447,19 +437,18 @@ func (b *outMsgBuilder) Put( map[Field]interface{}{ ChainID: chainID[:], RequestID: requestID, - ContainerID: containerID[:], + ContainerID: ids.Empty[:], // Populated for backwards compatibility ContainerBytes: container, }, - b.compress && Put.Compressible(), // Put messages may be compressed + b.compress && Put.Compressible(), false, ) } -func (b *outMsgBuilder) PushQuery( +func (b *outMsgBuilderWithPacker) PushQuery( chainID ids.ID, requestID uint32, deadline time.Duration, - containerID ids.ID, container []byte, ) (OutboundMessage, error) { return b.c.Pack( @@ -468,15 +457,15 @@ func (b *outMsgBuilder) PushQuery( ChainID: chainID[:], RequestID: requestID, Deadline: uint64(deadline), - ContainerID: containerID[:], + ContainerID: ids.Empty[:], // Populated for backwards compatibility ContainerBytes: container, }, - b.compress && PushQuery.Compressible(), // PushQuery messages may be compressed + b.compress && PushQuery.Compressible(), false, ) } -func (b *outMsgBuilder) PullQuery( +func (b *outMsgBuilderWithPacker) PullQuery( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -490,12 +479,12 @@ func (b *outMsgBuilder) PullQuery( Deadline: uint64(deadline), ContainerID: containerID[:], }, - PullQuery.Compressible(), // PullQuery messages can't be compressed + b.compress && PullQuery.Compressible(), false, ) } -func (b *outMsgBuilder) Chits( +func (b *outMsgBuilderWithPacker) Chits( chainID ids.ID, requestID uint32, containerIDs []ids.ID, @@ -509,35 +498,13 @@ func (b *outMsgBuilder) Chits( RequestID: requestID, ContainerIDs: containerIDBytes, }, - Chits.Compressible(), // Chits messages can't be compressed - false, - ) -} - -func (b *outMsgBuilder) ChitsV2( - chainID ids.ID, - requestID uint32, - containerIDs []ids.ID, - containerID ids.ID, -) (OutboundMessage, error) { - containerIDBytes := make([][]byte, len(containerIDs)) - encodeIDs(containerIDs, containerIDBytes) - - return b.c.Pack( - ChitsV2, - map[Field]interface{}{ - ChainID: chainID[:], - RequestID: requestID, - ContainerIDs: containerIDBytes, - ContainerID: containerID[:], - }, - ChitsV2.Compressible(), // ChitsV2 messages can't be compressed + b.compress && Chits.Compressible(), false, ) } // Application level request -func (b *outMsgBuilder) AppRequest( +func (b *outMsgBuilderWithPacker) AppRequest( chainID ids.ID, requestID uint32, deadline time.Duration, @@ -551,13 +518,13 @@ func (b *outMsgBuilder) AppRequest( Deadline: uint64(deadline), AppBytes: msg, }, - b.compress && AppRequest.Compressible(), // App messages may be compressed + b.compress && AppRequest.Compressible(), false, ) } // Application level response -func (b *outMsgBuilder) AppResponse(chainID ids.ID, requestID uint32, msg []byte) (OutboundMessage, error) { +func (b *outMsgBuilderWithPacker) AppResponse(chainID ids.ID, requestID uint32, msg []byte) (OutboundMessage, error) { return b.c.Pack( AppResponse, map[Field]interface{}{ @@ -565,20 +532,20 @@ func (b *outMsgBuilder) AppResponse(chainID ids.ID, requestID uint32, msg []byte RequestID: requestID, AppBytes: msg, }, - b.compress && AppResponse.Compressible(), // App messages may be compressed + b.compress && AppResponse.Compressible(), false, ) } // Application level gossiped message -func (b *outMsgBuilder) AppGossip(chainID ids.ID, msg []byte) (OutboundMessage, error) { +func (b *outMsgBuilderWithPacker) AppGossip(chainID ids.ID, msg []byte) (OutboundMessage, error) { return b.c.Pack( AppGossip, map[Field]interface{}{ ChainID: chainID[:], AppBytes: msg, }, - b.compress && AppGossip.Compressible(), // App messages may be compressed + b.compress && AppGossip.Compressible(), false, ) } diff --git a/avalanchego/message/outbound_msg_builder_proto.go b/avalanchego/message/outbound_msg_builder_proto.go new file mode 100644 index 00000000..a67c587e --- /dev/null +++ b/avalanchego/message/outbound_msg_builder_proto.go @@ -0,0 +1,517 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/ips" + + p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p" +) + +var _ OutboundMsgBuilder = &outMsgBuilderWithProto{} + +type outMsgBuilderWithProto struct { + compress bool // set to "true" if compression is enabled + + protoBuilder *msgBuilderProtobuf +} + +// Use "message.NewCreatorWithProto" to import this function +// since we do not expose "msgBuilderProtobuf" yet +func newOutboundBuilderWithProto(enableCompression bool, protoBuilder *msgBuilderProtobuf) OutboundMsgBuilder { + return &outMsgBuilderWithProto{ + compress: enableCompression, + protoBuilder: protoBuilder, + } +} + +func (b *outMsgBuilderWithProto) Version( + networkID uint32, + myTime uint64, + ip ips.IPPort, + myVersion string, + myVersionTime uint64, + sig []byte, + trackedSubnets []ids.ID, +) (OutboundMessage, error) { + subnetIDBytes := make([][]byte, len(trackedSubnets)) + for i, containerID := range trackedSubnets { + copy := containerID + subnetIDBytes[i] = copy[:] + } + return b.protoBuilder.createOutbound( + Version, + &p2ppb.Message{ + Message: &p2ppb.Message_Version{ + Version: &p2ppb.Version{ + NetworkId: networkID, + MyTime: myTime, + IpAddr: []byte(ip.IP.To16()), // ref. "wrappers.TryPackIP" + IpPort: uint32(ip.Port), + MyVersion: myVersion, + MyVersionTime: myVersionTime, + Sig: sig, + TrackedSubnets: subnetIDBytes, + }, + }, + }, + b.compress && Version.Compressible(), + true, + ) +} + +func (b *outMsgBuilderWithProto) PeerList(peers []ips.ClaimedIPPort, bypassThrottling bool) (OutboundMessage, error) { + claimIPPorts := make([]*p2ppb.ClaimedIpPort, len(peers)) + for i, p := range peers { + // ref. "wrappers.TryPackClaimedIPPortList", "PackX509Certificate" + claimIPPorts[i] = &p2ppb.ClaimedIpPort{ + // the inbound message parser will call "x509.ParseCertificate(p.X509Certificate)" + // to decode this message + X509Certificate: p.Cert.Raw, + IpAddr: []byte(p.IPPort.IP.To16()), + IpPort: uint32(p.IPPort.Port), + Timestamp: p.Timestamp, + Signature: p.Signature, + } + } + return b.protoBuilder.createOutbound( + PeerList, + &p2ppb.Message{ + Message: &p2ppb.Message_PeerList{ + PeerList: &p2ppb.PeerList{ + ClaimedIpPorts: claimIPPorts, + }, + }, + }, + b.compress && PeerList.Compressible(), + bypassThrottling, + ) +} + +func (b *outMsgBuilderWithProto) Ping() (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + Ping, + &p2ppb.Message{ + Message: &p2ppb.Message_Ping{ + Ping: &p2ppb.Ping{}, + }, + }, + b.compress && Ping.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) Pong(uptimePercentage uint8) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + Pong, + &p2ppb.Message{ + Message: &p2ppb.Message_Pong{ + Pong: &p2ppb.Pong{ + UptimePct: uint32(uptimePercentage), + }, + }, + }, + b.compress && Pong.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) GetStateSummaryFrontier( + chainID ids.ID, + requestID uint32, + deadline time.Duration, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + GetStateSummaryFrontier, + &p2ppb.Message{ + Message: &p2ppb.Message_GetStateSummaryFrontier{ + GetStateSummaryFrontier: &p2ppb.GetStateSummaryFrontier{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + }, + }, + }, + b.compress && GetStateSummaryFrontier.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) StateSummaryFrontier( + chainID ids.ID, + requestID uint32, + summary []byte, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + StateSummaryFrontier, + &p2ppb.Message{ + Message: &p2ppb.Message_StateSummaryFrontier_{ + StateSummaryFrontier_: &p2ppb.StateSummaryFrontier{ + ChainId: chainID[:], + RequestId: requestID, + Summary: summary, + }, + }, + }, + b.compress && StateSummaryFrontier.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) GetAcceptedStateSummary( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + heights []uint64, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + GetAcceptedStateSummary, + &p2ppb.Message{ + Message: &p2ppb.Message_GetAcceptedStateSummary{ + GetAcceptedStateSummary: &p2ppb.GetAcceptedStateSummary{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + Heights: heights, + }, + }, + }, + b.compress && GetAcceptedStateSummary.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) AcceptedStateSummary( + chainID ids.ID, + requestID uint32, + summaryIDs []ids.ID, +) (OutboundMessage, error) { + summaryIDBytes := make([][]byte, len(summaryIDs)) + encodeIDs(summaryIDs, summaryIDBytes) + return b.protoBuilder.createOutbound( + AcceptedStateSummary, + &p2ppb.Message{ + Message: &p2ppb.Message_AcceptedStateSummary_{ + AcceptedStateSummary_: &p2ppb.AcceptedStateSummary{ + ChainId: chainID[:], + RequestId: requestID, + SummaryIds: summaryIDBytes, + }, + }, + }, + b.compress && AcceptedStateSummary.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) GetAcceptedFrontier( + chainID ids.ID, + requestID uint32, + deadline time.Duration, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + GetAcceptedFrontier, + &p2ppb.Message{ + Message: &p2ppb.Message_GetAcceptedFrontier{ + GetAcceptedFrontier: &p2ppb.GetAcceptedFrontier{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + }, + }, + }, + b.compress && GetAcceptedFrontier.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) AcceptedFrontier( + chainID ids.ID, + requestID uint32, + containerIDs []ids.ID, +) (OutboundMessage, error) { + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return b.protoBuilder.createOutbound( + AcceptedFrontier, + &p2ppb.Message{ + Message: &p2ppb.Message_AcceptedFrontier_{ + AcceptedFrontier_: &p2ppb.AcceptedFrontier{ + ChainId: chainID[:], + RequestId: requestID, + ContainerIds: containerIDBytes, + }, + }, + }, + b.compress && AcceptedFrontier.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) GetAccepted( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + containerIDs []ids.ID, +) (OutboundMessage, error) { + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return b.protoBuilder.createOutbound( + GetAccepted, + &p2ppb.Message{ + Message: &p2ppb.Message_GetAccepted{ + GetAccepted: &p2ppb.GetAccepted{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + ContainerIds: containerIDBytes, + }, + }, + }, + b.compress && GetAccepted.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) Accepted( + chainID ids.ID, + requestID uint32, + containerIDs []ids.ID, +) (OutboundMessage, error) { + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return b.protoBuilder.createOutbound( + Accepted, + &p2ppb.Message{ + Message: &p2ppb.Message_Accepted_{ + Accepted_: &p2ppb.Accepted{ + ChainId: chainID[:], + RequestId: requestID, + ContainerIds: containerIDBytes, + }, + }, + }, + b.compress && Accepted.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) GetAncestors( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + containerID ids.ID, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + GetAncestors, + &p2ppb.Message{ + Message: &p2ppb.Message_GetAncestors{ + GetAncestors: &p2ppb.GetAncestors{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + ContainerId: containerID[:], + }, + }, + }, + b.compress && GetAncestors.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) Ancestors( + chainID ids.ID, + requestID uint32, + containers [][]byte, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + Ancestors, + &p2ppb.Message{ + Message: &p2ppb.Message_Ancestors_{ + Ancestors_: &p2ppb.Ancestors{ + ChainId: chainID[:], + RequestId: requestID, + Containers: containers, + }, + }, + }, + b.compress && Ancestors.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) Get( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + containerID ids.ID, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + Get, + &p2ppb.Message{ + Message: &p2ppb.Message_Get{ + Get: &p2ppb.Get{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + ContainerId: containerID[:], + }, + }, + }, + b.compress && Get.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) Put( + chainID ids.ID, + requestID uint32, + container []byte, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + Put, + &p2ppb.Message{ + Message: &p2ppb.Message_Put{ + Put: &p2ppb.Put{ + ChainId: chainID[:], + RequestId: requestID, + Container: container, + }, + }, + }, + b.compress && Put.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) PushQuery( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + container []byte, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + PushQuery, + &p2ppb.Message{ + Message: &p2ppb.Message_PushQuery{ + PushQuery: &p2ppb.PushQuery{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + Container: container, + }, + }, + }, + b.compress && PushQuery.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) PullQuery( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + containerID ids.ID, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + PullQuery, + &p2ppb.Message{ + Message: &p2ppb.Message_PullQuery{ + PullQuery: &p2ppb.PullQuery{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + ContainerId: containerID[:], + }, + }, + }, + b.compress && PullQuery.Compressible(), + false, + ) +} + +func (b *outMsgBuilderWithProto) Chits( + chainID ids.ID, + requestID uint32, + containerIDs []ids.ID, +) (OutboundMessage, error) { + containerIDBytes := make([][]byte, len(containerIDs)) + encodeIDs(containerIDs, containerIDBytes) + return b.protoBuilder.createOutbound( + Chits, + &p2ppb.Message{ + Message: &p2ppb.Message_Chits{ + Chits: &p2ppb.Chits{ + ChainId: chainID[:], + RequestId: requestID, + ContainerIds: containerIDBytes, + }, + }, + }, + b.compress && Chits.Compressible(), + false, + ) +} + +// Application level request +func (b *outMsgBuilderWithProto) AppRequest( + chainID ids.ID, + requestID uint32, + deadline time.Duration, + msg []byte, +) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + AppRequest, + &p2ppb.Message{ + Message: &p2ppb.Message_AppRequest{ + AppRequest: &p2ppb.AppRequest{ + ChainId: chainID[:], + RequestId: requestID, + Deadline: uint64(deadline), + AppBytes: msg, + }, + }, + }, + b.compress && AppRequest.Compressible(), + false, + ) +} + +// Application level response +func (b *outMsgBuilderWithProto) AppResponse(chainID ids.ID, requestID uint32, msg []byte) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + AppResponse, + &p2ppb.Message{ + Message: &p2ppb.Message_AppResponse{ + AppResponse: &p2ppb.AppResponse{ + ChainId: chainID[:], + RequestId: requestID, + AppBytes: msg, + }, + }, + }, + b.compress && AppResponse.Compressible(), + false, + ) +} + +// Application level gossiped message +func (b *outMsgBuilderWithProto) AppGossip(chainID ids.ID, msg []byte) (OutboundMessage, error) { + return b.protoBuilder.createOutbound( + AppGossip, + &p2ppb.Message{ + Message: &p2ppb.Message_AppGossip{ + AppGossip: &p2ppb.AppGossip{ + ChainId: chainID[:], + AppBytes: msg, + }, + }, + }, + b.compress && AppGossip.Compressible(), + false, + ) +} diff --git a/avalanchego/message/outbound_msg_builder_proto_test.go b/avalanchego/message/outbound_msg_builder_proto_test.go new file mode 100644 index 00000000..9e1af16a --- /dev/null +++ b/avalanchego/message/outbound_msg_builder_proto_test.go @@ -0,0 +1,31 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" +) + +func Test_newOutboundBuilderWithProto(t *testing.T) { + t.Parallel() + require := require.New(t) + + mb, err := newMsgBuilderProtobuf("test", prometheus.NewRegistry(), int64(constants.DefaultMaxMessageSize), 5*time.Second) + require.NoError(err) + + builder := newOutboundBuilderWithProto(true /*compress*/, mb) + + outMsg, err := builder.GetAcceptedStateSummary(ids.GenerateTestID(), uint32(12345), time.Hour, []uint64{1000, 2000}) + require.NoError(err) + + t.Logf("outbound message built %q with size %d", outMsg.Op().String(), len(outMsg.Bytes())) +} diff --git a/avalanchego/message/test_message.go b/avalanchego/message/test_message.go new file mode 100644 index 00000000..92d9dec1 --- /dev/null +++ b/avalanchego/message/test_message.go @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +type TestMsg struct { + op Op + bytes []byte + bypassThrottling bool +} + +func NewTestMsg(op Op, bytes []byte, bypassThrottling bool) *TestMsg { + return &TestMsg{ + op: op, + bytes: bytes, + bypassThrottling: bypassThrottling, + } +} + +func (m *TestMsg) Op() Op { return m.op } +func (*TestMsg) Get(Field) interface{} { return nil } +func (m *TestMsg) Bytes() []byte { return m.bytes } +func (*TestMsg) BytesSavedCompression() int { return 0 } +func (*TestMsg) AddRef() {} +func (*TestMsg) DecRef() {} +func (*TestMsg) IsProto() bool { return false } +func (m *TestMsg) BypassThrottling() bool { return m.bypassThrottling } diff --git a/avalanchego/nat/nat.go b/avalanchego/nat/nat.go index 4633212a..9520921f 100644 --- a/avalanchego/nat/nat.go +++ b/avalanchego/nat/nat.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nat diff --git a/avalanchego/nat/no_router.go b/avalanchego/nat/no_router.go index 2e93039a..32a089dd 100644 --- a/avalanchego/nat/no_router.go +++ b/avalanchego/nat/no_router.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nat diff --git a/avalanchego/nat/pmp.go b/avalanchego/nat/pmp.go index cf0790c1..57df17b7 100644 --- a/avalanchego/nat/pmp.go +++ b/avalanchego/nat/pmp.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nat diff --git a/avalanchego/nat/upnp.go b/avalanchego/nat/upnp.go index 8d54ce53..93fcf034 100644 --- a/avalanchego/nat/upnp.go +++ b/avalanchego/nat/upnp.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nat diff --git a/avalanchego/network/certs_test.go b/avalanchego/network/certs_test.go index a7f2b358..fd556021 100644 --- a/avalanchego/network/certs_test.go +++ b/avalanchego/network/certs_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network @@ -28,7 +28,7 @@ func getTLS(t *testing.T, index int) (ids.NodeID, *tls.Certificate, *tls.Config) if err != nil { t.Fatal(err) } - tlsConfig := peer.TLSConfig(*cert) + tlsConfig := peer.TLSConfig(*cert, nil) tlsCerts = append(tlsCerts, cert) tlsConfigs = append(tlsConfigs, tlsConfig) diff --git a/avalanchego/network/config.go b/avalanchego/network/config.go index e7466e0c..c963ad5f 100644 --- a/avalanchego/network/config.go +++ b/avalanchego/network/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network @@ -107,6 +107,8 @@ type Config struct { DialerConfig dialer.Config `json:"dialerConfig"` TLSConfig *tls.Config `json:"-"` + TLSKeyLogFile string `json:"tlsKeyLogFile"` + Namespace string `json:"namespace"` MyNodeID ids.NodeID `json:"myNodeID"` MyIPPort ips.DynamicIPPort `json:"myIP"` diff --git a/avalanchego/network/conn_test.go b/avalanchego/network/conn_test.go index 79afe2a7..ad97c759 100644 --- a/avalanchego/network/conn_test.go +++ b/avalanchego/network/conn_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/network/dialer/dialer.go b/avalanchego/network/dialer/dialer.go index c295d6ad..358d1852 100644 --- a/avalanchego/network/dialer/dialer.go +++ b/avalanchego/network/dialer/dialer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package dialer diff --git a/avalanchego/network/dialer/dialer_test.go b/avalanchego/network/dialer/dialer_test.go index 31c2dcc3..f2debb61 100644 --- a/avalanchego/network/dialer/dialer_test.go +++ b/avalanchego/network/dialer/dialer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package dialer diff --git a/avalanchego/network/dialer_test.go b/avalanchego/network/dialer_test.go index 418339f6..f3ca1c00 100644 --- a/avalanchego/network/dialer_test.go +++ b/avalanchego/network/dialer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/network/handler_test.go b/avalanchego/network/handler_test.go index 1913a977..5879edc5 100644 --- a/avalanchego/network/handler_test.go +++ b/avalanchego/network/handler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/network/ip_signer.go b/avalanchego/network/ip_signer.go index 64cf16c8..4b543f37 100644 --- a/avalanchego/network/ip_signer.go +++ b/avalanchego/network/ip_signer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/network/ip_signer_test.go b/avalanchego/network/ip_signer_test.go index 6b1ec74e..3872c931 100644 --- a/avalanchego/network/ip_signer_test.go +++ b/avalanchego/network/ip_signer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/network/listener_test.go b/avalanchego/network/listener_test.go index aacaaf14..1b8b78d9 100644 --- a/avalanchego/network/listener_test.go +++ b/avalanchego/network/listener_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/network/metrics.go b/avalanchego/network/metrics.go index 734a81ee..5d30a2d0 100644 --- a/avalanchego/network/metrics.go +++ b/avalanchego/network/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network @@ -22,6 +22,7 @@ type metrics struct { sendFailRate prometheus.Gauge connected prometheus.Counter disconnected prometheus.Counter + acceptFailed *prometheus.CounterVec inboundConnRateLimited prometheus.Counter inboundConnAllowed prometheus.Counter nodeUptimeWeightedAverage prometheus.Gauge @@ -78,6 +79,11 @@ func newMetrics(namespace string, registerer prometheus.Registerer, initialSubne Name: "times_disconnected", Help: "Times this node disconnected from a peer it had completed a handshake with", }), + acceptFailed: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: namespace, + Name: "accept_failed", + Help: "Times this node failed to accept connection from a peer it had completed a handshake with", + }, []string{"error"}), inboundConnAllowed: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: namespace, Name: "inbound_conn_throttler_allowed", @@ -111,6 +117,7 @@ func newMetrics(namespace string, registerer prometheus.Registerer, initialSubne registerer.Register(m.sendFailRate), registerer.Register(m.connected), registerer.Register(m.disconnected), + registerer.Register(m.acceptFailed), registerer.Register(m.inboundConnAllowed), registerer.Register(m.inboundConnRateLimited), registerer.Register(m.nodeUptimeWeightedAverage), @@ -126,6 +133,10 @@ func newMetrics(namespace string, registerer prometheus.Registerer, initialSubne // initialize to 0 m.numSubnetPeers.WithLabelValues(subnetID.String()).Set(0) } + + // initialize to 0 + _ = m.acceptFailed.WithLabelValues("timeout") + _ = m.acceptFailed.WithLabelValues("temporary") return m, errs.Err } diff --git a/avalanchego/network/network.go b/avalanchego/network/network.go index a770bd41..53111d77 100644 --- a/avalanchego/network/network.go +++ b/avalanchego/network/network.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network @@ -153,6 +153,8 @@ type network struct { func NewNetwork( config *Config, msgCreator message.Creator, + msgCreatorWithProto message.Creator, + banffTime time.Time, // TODO: remove this once we complete banff migration metricsRegisterer prometheus.Registerer, log logging.Logger, listener net.Listener, @@ -199,16 +201,16 @@ func NewNetwork( return nil, fmt.Errorf("initializing network metrics failed with: %w", err) } - pingMessge, err := msgCreator.Ping() - if err != nil { - return nil, fmt.Errorf("initializing common ping message failed with: %w", err) - } - peerConfig := &peer.Config{ - ReadBufferSize: config.PeerReadBufferSize, - WriteBufferSize: config.PeerWriteBufferSize, - Metrics: peerMetrics, - MessageCreator: msgCreator, + ReadBufferSize: config.PeerReadBufferSize, + WriteBufferSize: config.PeerWriteBufferSize, + Metrics: peerMetrics, + MessageCreator: msgCreator, + MessageCreatorWithProto: msgCreatorWithProto, + + // TODO: remove this once we complete banff migration + BanffTime: banffTime, + Log: log, InboundMsgThrottler: inboundMsgThrottler, Network: nil, // This is set below. @@ -221,8 +223,8 @@ func NewNetwork( PongTimeout: config.PingPongTimeout, MaxClockDifference: config.MaxClockDifference, ResourceTracker: config.ResourceTracker, - PingMessage: pingMessge, } + onCloseCtx, cancel := context.WithCancel(context.Background()) n := &network{ config: config, @@ -470,7 +472,7 @@ func (n *network) Version() (message.OutboundMessage, error) { if err != nil { return nil, err } - return n.peerConfig.MessageCreator.Version( + return n.peerConfig.GetMessageCreator().Version( n.peerConfig.NetworkID, n.peerConfig.Clock.Unix(), mySignedIP.IP.IP, @@ -483,7 +485,7 @@ func (n *network) Version() (message.OutboundMessage, error) { func (n *network) Peers() (message.OutboundMessage, error) { peers := n.sampleValidatorIPs() - return n.peerConfig.MessageCreator.PeerList(peers, true) + return n.peerConfig.GetMessageCreator().PeerList(peers, true) } func (n *network) Pong(nodeID ids.NodeID) (message.OutboundMessage, error) { @@ -493,7 +495,7 @@ func (n *network) Pong(nodeID ids.NodeID) (message.OutboundMessage, error) { } uptimePercentInt := uint8(uptimePercentFloat * 100) - return n.peerConfig.MessageCreator.Pong(uptimePercentInt) + return n.peerConfig.GetMessageCreator().Pong(uptimePercentInt) } // Dispatch starts accepting connections from other nodes attempting to connect @@ -505,11 +507,20 @@ func (n *network) Dispatch() error { for { // Continuously accept new connections conn, err := n.listener.Accept() // Returns error when n.Close() is called if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Temporary() { - // Sleep for a small amount of time to try to wait for the - // temporary error to go away. - time.Sleep(time.Millisecond) - continue + if netErr, ok := err.(net.Error); ok { + if netErr.Timeout() { + n.metrics.acceptFailed.WithLabelValues("timeout").Inc() + } + + // TODO: deprecate "Temporary" and use "Timeout" + if netErr.Temporary() { + n.metrics.acceptFailed.WithLabelValues("temporary").Inc() + + // Sleep for a small amount of time to try to wait for the + // temporary error to go away. + time.Sleep(time.Millisecond) + continue + } } n.peerConfig.Log.Debug("error during server accept", @@ -1149,7 +1160,7 @@ func (n *network) runTimers() { continue } - msg, err := n.peerConfig.MessageCreator.PeerList(validatorIPs, false) + msg, err := n.peerConfig.GetMessageCreator().PeerList(validatorIPs, false) if err != nil { n.peerConfig.Log.Error( "failed to gossip", diff --git a/avalanchego/network/network_test.go b/avalanchego/network/network_test.go index c0a8e090..5c3dbc9d 100644 --- a/avalanchego/network/network_test.go +++ b/avalanchego/network/network_test.go @@ -1,10 +1,11 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network import ( "crypto" + "fmt" "net" "sync" "testing" @@ -169,16 +170,26 @@ func newTestNetwork(t *testing.T, count int) (*testDialer, []*testListener, []id return dialer, listeners, nodeIDs, configs } -func newMessageCreator(t *testing.T) message.Creator { +func newMessageCreator(t *testing.T) (message.Creator, message.Creator) { t.Helper() + mc, err := message.NewCreator( prometheus.NewRegistry(), + "", true, + 10*time.Second, + ) + require.NoError(t, err) + + mcProto, err := message.NewCreatorWithProto( + prometheus.NewRegistry(), "", + true, 10*time.Second, ) require.NoError(t, err) - return mc + + return mc, mcProto } func newFullyConnectedTestNetwork(t *testing.T, handlers []router.InboundHandler) ([]ids.NodeID, []Network, *sync.WaitGroup) { @@ -196,7 +207,7 @@ func newFullyConnectedTestNetwork(t *testing.T, handlers []router.InboundHandler require.NoError(err) } - msgCreator := newMessageCreator(t) + msgCreator, msgCreatorWithProto := newMessageCreator(t) var ( networks = make([]Network, len(configs)) @@ -216,6 +227,8 @@ func newFullyConnectedTestNetwork(t *testing.T, handlers []router.InboundHandler net, err := NewNetwork( config, msgCreator, + msgCreatorWithProto, + time.Now().Add(time.Hour), // TODO: test proto with banff activated prometheus.NewRegistry(), logging.NoLog{}, listeners[i], @@ -278,7 +291,6 @@ func newFullyConnectedTestNetwork(t *testing.T, handlers []router.InboundHandler func TestNewNetwork(t *testing.T) { _, networks, wg := newFullyConnectedTestNetwork(t, []router.InboundHandler{nil, nil, nil}) - for _, net := range networks { net.StartClose() } @@ -288,40 +300,52 @@ func TestNewNetwork(t *testing.T) { func TestSend(t *testing.T) { require := require.New(t) - received := make(chan message.InboundMessage) - nodeIDs, networks, wg := newFullyConnectedTestNetwork( - t, - []router.InboundHandler{ - router.InboundHandlerFunc(func(message.InboundMessage) { - t.Fatal("unexpected message received") - }), - router.InboundHandlerFunc(func(msg message.InboundMessage) { - received <- msg - }), - router.InboundHandlerFunc(func(message.InboundMessage) { - t.Fatal("unexpected message received") - }), - }, - ) - - net0 := networks[0] - - mc := newMessageCreator(t) - outboundGetMsg, err := mc.Get(ids.Empty, 1, time.Second, ids.Empty) - require.NoError(err) + for _, useProto := range []bool{false, true} { + t.Run(fmt.Sprintf("use proto buf message creator %v", useProto), func(tt *testing.T) { + received := make(chan message.InboundMessage) + nodeIDs, networks, wg := newFullyConnectedTestNetwork( + tt, + []router.InboundHandler{ + router.InboundHandlerFunc(func(message.InboundMessage) { + tt.Fatal("unexpected message received") + }), + router.InboundHandlerFunc(func(msg message.InboundMessage) { + received <- msg + }), + router.InboundHandlerFunc(func(message.InboundMessage) { + tt.Fatal("unexpected message received") + }), + }, + ) + + net0 := networks[0] + + mc, mcProto := newMessageCreator(tt) + var ( + outboundGetMsg message.OutboundMessage + err error + ) + if !useProto { + outboundGetMsg, err = mc.Get(ids.Empty, 1, time.Second, ids.Empty) + } else { + outboundGetMsg, err = mcProto.Get(ids.Empty, 1, time.Second, ids.Empty) + } + require.NoError(err) - toSend := ids.NodeIDSet{} - toSend.Add(nodeIDs[1]) - sentTo := net0.Send(outboundGetMsg, toSend, constants.PrimaryNetworkID, false) - require.EqualValues(toSend, sentTo) + toSend := ids.NodeIDSet{} + toSend.Add(nodeIDs[1]) + sentTo := net0.Send(outboundGetMsg, toSend, constants.PrimaryNetworkID, false) + require.EqualValues(toSend, sentTo) - inboundGetMsg := <-received - require.Equal(message.Get, inboundGetMsg.Op()) + inboundGetMsg := <-received + require.Equal(message.Get, inboundGetMsg.Op()) - for _, net := range networks { - net.StartClose() + for _, net := range networks { + net.StartClose() + } + wg.Wait() + }) } - wg.Wait() } func TestTrackVerifiesSignatures(t *testing.T) { diff --git a/avalanchego/network/peer/config.go b/avalanchego/network/peer/config.go index 13d881d7..3821b548 100644 --- a/avalanchego/network/peer/config.go +++ b/avalanchego/network/peer/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer @@ -21,10 +21,15 @@ type Config struct { // Size, in bytes, of the buffer this peer reads messages into ReadBufferSize int // Size, in bytes, of the buffer this peer writes messages into - WriteBufferSize int - Clock mockable.Clock - Metrics *Metrics - MessageCreator message.Creator + WriteBufferSize int + Clock mockable.Clock + Metrics *Metrics + MessageCreator message.Creator + MessageCreatorWithProto message.Creator + + // TODO: remove this once we complete banff migration + BanffTime time.Time + Log logging.Logger InboundMsgThrottler throttling.InboundMsgThrottler Network Network @@ -43,6 +48,16 @@ type Config struct { // Tracks CPU/disk usage caused by each peer. ResourceTracker tracker.ResourceTracker +} + +func (c *Config) GetMessageCreator() message.Creator { + now := c.Clock.Time() + if c.IsBanffActivated(now) { + return c.MessageCreatorWithProto + } + return c.MessageCreator +} - PingMessage message.OutboundMessage +func (c *Config) IsBanffActivated(time time.Time) bool { + return !time.Before(c.BanffTime) } diff --git a/avalanchego/network/peer/example_test.go b/avalanchego/network/peer/example_test.go index 3eabdc23..8530fd48 100644 --- a/avalanchego/network/peer/example_test.go +++ b/avalanchego/network/peer/example_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/info.go b/avalanchego/network/peer/info.go index 69b42784..ec48bbb3 100644 --- a/avalanchego/network/peer/info.go +++ b/avalanchego/network/peer/info.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/ip.go b/avalanchego/network/peer/ip.go index cf9c825b..f441eaf9 100644 --- a/avalanchego/network/peer/ip.go +++ b/avalanchego/network/peer/ip.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/message_queue.go b/avalanchego/network/peer/message_queue.go index 08a3d636..c8755128 100644 --- a/avalanchego/network/peer/message_queue.go +++ b/avalanchego/network/peer/message_queue.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer @@ -12,9 +12,12 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/network/throttling" + "github.com/ava-labs/avalanchego/utils/buffer" "github.com/ava-labs/avalanchego/utils/logging" ) +const initialQueueSize = 64 + var ( _ MessageQueue = &throttledMessageQueue{} _ MessageQueue = &blockingMessageQueue{} @@ -64,7 +67,7 @@ type throttledMessageQueue struct { // queue of the messages // [cond.L] must be held while accessing [queue]. - queue []message.OutboundMessage + queue buffer.UnboundedQueue[message.OutboundMessage] } func NewThrottledMessageQueue( @@ -78,8 +81,8 @@ func NewThrottledMessageQueue( id: id, log: log, outboundMsgThrottler: outboundMsgThrottler, - - cond: sync.NewCond(&sync.Mutex{}), + cond: sync.NewCond(&sync.Mutex{}), + queue: buffer.NewUnboundedSliceQueue[message.OutboundMessage](initialQueueSize), } } @@ -126,7 +129,7 @@ func (q *throttledMessageQueue) Push(ctx context.Context, msg message.OutboundMe return false } - q.queue = append(q.queue, msg) + q.queue.Enqueue(msg) q.cond.Signal() return true } @@ -139,7 +142,7 @@ func (q *throttledMessageQueue) Pop() (message.OutboundMessage, bool) { if q.closed { return nil, false } - if len(q.queue) > 0 { + if q.queue.Len() > 0 { // There is a message break } @@ -154,7 +157,7 @@ func (q *throttledMessageQueue) PopNow() (message.OutboundMessage, bool) { q.cond.L.Lock() defer q.cond.L.Unlock() - if len(q.queue) == 0 { + if q.closed || q.queue.Len() == 0 { // There isn't a message return nil, false } @@ -163,9 +166,7 @@ func (q *throttledMessageQueue) PopNow() (message.OutboundMessage, bool) { } func (q *throttledMessageQueue) pop() message.OutboundMessage { - msg := q.queue[0] - q.queue[0] = nil - q.queue = q.queue[1:] + msg, _ := q.queue.Dequeue() q.outboundMsgThrottler.Release(msg, q.id) return msg @@ -175,9 +176,14 @@ func (q *throttledMessageQueue) Close() { q.cond.L.Lock() defer q.cond.L.Unlock() + if q.closed { + return + } + q.closed = true - for _, msg := range q.queue { + for q.queue.Len() > 0 { + msg, _ := q.queue.Dequeue() q.outboundMsgThrottler.Release(msg, q.id) q.onFailed.SendFailed(msg) } diff --git a/avalanchego/network/peer/message_queue_test.go b/avalanchego/network/peer/message_queue_test.go index 3a87714a..1db29108 100644 --- a/avalanchego/network/peer/message_queue_test.go +++ b/avalanchego/network/peer/message_queue_test.go @@ -1,10 +1,11 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer import ( "context" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -16,35 +17,48 @@ import ( func TestBlockingMessageQueue(t *testing.T) { require := require.New(t) - q := NewBlockingMessageQueue( - SendFailedFunc(func(msg message.OutboundMessage) { - t.Fail() - }), - logging.NoLog{}, - 0, - ) - - mc := newMessageCreator(t) - msg, err := mc.Ping() - require.NoError(err) - - numToSend := 10 - go func() { - for i := 0; i < numToSend; i++ { - q.Push(context.Background(), msg) - } - }() - - for i := 0; i < numToSend; i++ { - _, ok := q.Pop() - require.True(ok) - } + for _, useProto := range []bool{false, true} { + t.Run(fmt.Sprintf("use proto buf message creator %v", useProto), func(tt *testing.T) { + q := NewBlockingMessageQueue( + SendFailedFunc(func(msg message.OutboundMessage) { + t.Fail() + }), + logging.NoLog{}, + 0, + ) + + mc, mcProto := newMessageCreator(tt) + + var ( + msg message.OutboundMessage + err error + ) + if useProto { + msg, err = mcProto.Ping() + } else { + msg, err = mc.Ping() + } + require.NoError(err) - _, ok := q.PopNow() - require.False(ok) + numToSend := 10 + go func() { + for i := 0; i < numToSend; i++ { + q.Push(context.Background(), msg) + } + }() - q.Close() + for i := 0; i < numToSend; i++ { + _, ok := q.Pop() + require.True(ok) + } - _, ok = q.Pop() - require.False(ok) + _, ok := q.PopNow() + require.False(ok) + + q.Close() + + _, ok = q.Pop() + require.False(ok) + }) + } } diff --git a/avalanchego/network/peer/metrics.go b/avalanchego/network/peer/metrics.go index cfd88664..6eef6f23 100644 --- a/avalanchego/network/peer/metrics.go +++ b/avalanchego/network/peer/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/msg_length.go b/avalanchego/network/peer/msg_length.go index fa593a3d..3d65b382 100644 --- a/avalanchego/network/peer/msg_length.go +++ b/avalanchego/network/peer/msg_length.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/network.go b/avalanchego/network/peer/network.go index e0696c6b..b38a4827 100644 --- a/avalanchego/network/peer/network.go +++ b/avalanchego/network/peer/network.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/peer.go b/avalanchego/network/peer/peer.go index 023c77f2..9a567468 100644 --- a/avalanchego/network/peer/peer.go +++ b/avalanchego/network/peer/peer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer @@ -339,10 +339,6 @@ func (p *peer) readMessages() { ) return } - if isProto { - p.Log.Debug("unexpected isProto=true from 'readMsgLen' (not implemented yet)") - return - } // Wait until the throttler says we can proceed to read the message. // @@ -400,15 +396,20 @@ func (p *peer) readMessages() { p.Log.Verbo("parsing message", zap.Stringer("nodeID", p.id), - zap.Binary("message", msgBytes), + zap.Binary("messageBytes", msgBytes), ) // Parse the message - msg, err := p.MessageCreator.Parse(msgBytes, p.id, onFinishedHandling) + var msg message.InboundMessage + if isProto { + msg, err = p.MessageCreatorWithProto.Parse(msgBytes, p.id, onFinishedHandling) + } else { + msg, err = p.MessageCreator.Parse(msgBytes, p.id, onFinishedHandling) + } if err != nil { p.Log.Verbo("failed to parse message", zap.Stringer("nodeID", p.id), - zap.Binary("message", msgBytes), + zap.Binary("messageBytes", msgBytes), zap.Error(err), ) @@ -442,7 +443,13 @@ func (p *peer) writeMessages() { // Make sure that the version is the first message sent msg, err := p.Network.Version() - p.Log.AssertNoError(err) + if err != nil { + p.Log.Error("failed to create message", + zap.Stringer("messageOp", message.Version), + zap.Error(err), + ) + return + } p.writeMessage(writer, msg) @@ -477,7 +484,7 @@ func (p *peer) writeMessage(writer io.Writer, msg message.OutboundMessage) { msgBytes := msg.Bytes() p.Log.Verbo("sending message", zap.Stringer("nodeID", p.id), - zap.Binary("message", msgBytes), + zap.Binary("messageBytes", msgBytes), ) if err := p.conn.SetWriteDeadline(p.nextTimeout()); err != nil { @@ -490,7 +497,8 @@ func (p *peer) writeMessage(writer io.Writer, msg message.OutboundMessage) { } msgLen := uint32(len(msgBytes)) - msgLenBytes, err := writeMsgLen(msgLen, false /* true to use protobufs */, constants.DefaultMaxMessageSize) + isProto := msg.IsProto() + msgLenBytes, err := writeMsgLen(msgLen, isProto, constants.DefaultMaxMessageSize) if err != nil { p.Log.Verbo("error writing message length", zap.Stringer("nodeID", p.id), @@ -548,8 +556,16 @@ func (p *peer) sendPings() { } } - p.Config.PingMessage.AddRef() - p.Send(p.onClosingCtx, p.Config.PingMessage) + pingMessage, err := p.Config.GetMessageCreator().Ping() + if err != nil { + p.Log.Error("failed to create message", + zap.Stringer("messageOp", message.Ping), + zap.Error(err), + ) + return + } + + p.Send(p.onClosingCtx, pingMessage) case <-p.onClosingCtx.Done(): return } @@ -593,13 +609,36 @@ func (p *peer) handle(msg message.InboundMessage) { func (p *peer) handlePing(_ message.InboundMessage) { msg, err := p.Network.Pong(p.id) - p.Log.AssertNoError(err) + if err != nil { + p.Log.Error("failed to create message", + zap.Stringer("messageOp", message.Pong), + zap.Error(err), + ) + return + } p.Send(p.onClosingCtx, msg) } func (p *peer) handlePong(msg message.InboundMessage) { - uptime := msg.Get(message.Uptime).(uint8) + uptimeIntf, err := msg.Get(message.Uptime) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Pong), + zap.Stringer("field", message.Uptime), + zap.Error(err), + ) + p.StartClose() + return + } + + uptime := uptimeIntf.(uint8) if uptime > 100 { + p.Log.Debug("dropping pong message with invalid uptime", + zap.Stringer("nodeID", p.id), + zap.Uint8("uptime", uptime), + ) + p.StartClose() return } @@ -610,13 +649,27 @@ func (p *peer) handlePong(msg message.InboundMessage) { func (p *peer) handleVersion(msg message.InboundMessage) { if p.gotVersion.GetValue() { + // TODO: this should never happen, should we close the connection here? p.Log.Verbo("dropping duplicated version message", zap.Stringer("nodeID", p.id), ) return } - if peerNetworkID := msg.Get(message.NetworkID).(uint32); peerNetworkID != p.NetworkID { + peerNetworkIDIntf, err := msg.Get(message.NetworkID) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Version), + zap.Stringer("field", message.NetworkID), + zap.Error(err), + ) + p.StartClose() + return + } + peerNetworkID := peerNetworkIDIntf.(uint32) + + if peerNetworkID != p.NetworkID { p.Log.Debug("networkID mismatch", zap.Stringer("nodeID", p.id), zap.Uint32("peerNetworkID", peerNetworkID), @@ -626,27 +679,51 @@ func (p *peer) handleVersion(msg message.InboundMessage) { return } - myTime := float64(p.Clock.Unix()) - peerTime := float64(msg.Get(message.MyTime).(uint64)) - if math.Abs(peerTime-myTime) > p.MaxClockDifference.Seconds() { + peerTimeIntf, err := msg.Get(message.MyTime) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Version), + zap.Stringer("field", message.MyTime), + zap.Error(err), + ) + p.StartClose() + return + } + peerTime := peerTimeIntf.(uint64) + + myTime := p.Clock.Unix() + if math.Abs(float64(peerTime)-float64(myTime)) > p.MaxClockDifference.Seconds() { if p.Beacons.Contains(p.id) { p.Log.Warn("beacon reports out of sync time", - zap.Stringer("beaconID", p.id), - zap.Uint64("beaconTime", uint64(peerTime)), - zap.Uint64("ourTime", uint64(myTime)), + zap.Stringer("nodeID", p.id), + zap.Uint64("peerTime", peerTime), + zap.Uint64("myTime", myTime), ) } else { p.Log.Debug("peer reports out of sync time", - zap.Stringer("beaconID", p.id), - zap.Uint64("beaconTime", uint64(peerTime)), - zap.Uint64("ourTime", uint64(myTime)), + zap.Stringer("nodeID", p.id), + zap.Uint64("peerTime", peerTime), + zap.Uint64("myTime", myTime), ) } p.StartClose() return } - peerVersionStr := msg.Get(message.VersionStr).(string) + peerVersionStrIntf, err := msg.Get(message.VersionStr) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Version), + zap.Stringer("field", message.VersionStr), + zap.Error(err), + ) + p.StartClose() + return + } + peerVersionStr := peerVersionStrIntf.(string) + peerVersion, err := version.ParseApplication(peerVersionStr) if err != nil { p.Log.Debug("failed to parse peer version", @@ -682,11 +759,23 @@ func (p *peer) handleVersion(msg message.InboundMessage) { return } + versionTimeIntf, err := msg.Get(message.VersionTime) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Version), + zap.Stringer("field", message.VersionTime), + zap.Error(err), + ) + p.StartClose() + return + } + versionTime := versionTimeIntf.(uint64) + // Note that it is expected that the [versionTime] can be in the past. We // are just verifying that the claimed signing time isn't too far in the // future here. - versionTime := msg.Get(message.VersionTime).(uint64) - if float64(versionTime)-myTime > p.MaxClockDifference.Seconds() { + if float64(versionTime)-float64(myTime) > p.MaxClockDifference.Seconds() { p.Log.Debug("peer attempting to connect with version timestamp too far in the future", zap.Stringer("nodeID", p.id), zap.Uint64("versionTime", versionTime), @@ -695,10 +784,20 @@ func (p *peer) handleVersion(msg message.InboundMessage) { return } - peerIP := msg.Get(message.IP).(ips.IPPort) - // handle subnet IDs - subnetIDsBytes := msg.Get(message.TrackedSubnets).([][]byte) + subnetIDsBytesIntf, err := msg.Get(message.TrackedSubnets) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Version), + zap.Stringer("field", message.TrackedSubnets), + zap.Error(err), + ) + p.StartClose() + return + } + subnetIDsBytes := subnetIDsBytesIntf.([][]byte) + for _, subnetIDBytes := range subnetIDsBytes { subnetID, err := ids.ToID(subnetIDBytes) if err != nil { @@ -715,12 +814,38 @@ func (p *peer) handleVersion(msg message.InboundMessage) { } } + peerIPIntf, err := msg.Get(message.IP) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Version), + zap.Stringer("field", message.IP), + zap.Error(err), + ) + p.StartClose() + return + } + peerIP := peerIPIntf.(ips.IPPort) + + signatureIntf, err := msg.Get(message.SigBytes) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.Version), + zap.Stringer("field", message.SigBytes), + zap.Error(err), + ) + p.StartClose() + return + } + signature := signatureIntf.([]byte) + p.ip = &SignedIP{ IP: UnsignedIP{ IP: peerIP, Timestamp: versionTime, }, - Signature: msg.Get(message.SigBytes).([]byte), + Signature: signature, } if err := p.ip.Verify(p.cert); err != nil { p.Log.Debug("signature verification failed", @@ -734,7 +859,13 @@ func (p *peer) handleVersion(msg message.InboundMessage) { p.gotVersion.SetValue(true) peerlistMsg, err := p.Network.Peers() - p.Log.AssertNoError(err) + if err != nil { + p.Log.Error("failed to create message", + zap.Stringer("messageOp", message.PeerList), + zap.Error(err), + ) + return + } p.Send(p.onClosingCtx, peerlistMsg) } @@ -749,7 +880,19 @@ func (p *peer) handlePeerList(msg message.InboundMessage) { close(p.onFinishHandshake) } - ips := msg.Get(message.Peers).([]ips.ClaimedIPPort) + ipsIntf, err := msg.Get(message.Peers) + if err != nil { + p.Log.Debug("message with invalid field", + zap.Stringer("nodeID", p.id), + zap.Stringer("messageOp", message.PeerList), + zap.Stringer("field", message.Peers), + zap.Error(err), + ) + p.StartClose() + return + } + ips := ipsIntf.([]ips.ClaimedIPPort) + for _, ip := range ips { if !p.Network.Track(ip) { p.Metrics.NumUselessPeerListBytes.Add(float64(ip.BytesLen())) diff --git a/avalanchego/network/peer/peer_test.go b/avalanchego/network/peer/peer_test.go index 5587b528..a76ec0bc 100644 --- a/avalanchego/network/peer/peer_test.go +++ b/avalanchego/network/peer/peer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer @@ -7,6 +7,7 @@ import ( "context" "crypto" "crypto/x509" + "fmt" "net" "testing" "time" @@ -43,16 +44,26 @@ type rawTestPeer struct { inboundMsgChan <-chan message.InboundMessage } -func newMessageCreator(t *testing.T) message.Creator { +func newMessageCreator(t *testing.T) (message.Creator, message.Creator) { t.Helper() + mc, err := message.NewCreator( prometheus.NewRegistry(), + "", true, + 10*time.Second, + ) + require.NoError(t, err) + + mcProto, err := message.NewCreatorWithProto( + prometheus.NewRegistry(), "", + true, 10*time.Second, ) require.NoError(t, err) - return mc + + return mc, mcProto } func makeRawTestPeers(t *testing.T) (*rawTestPeer, *rawTestPeer) { @@ -70,10 +81,7 @@ func makeRawTestPeers(t *testing.T) (*rawTestPeer, *rawTestPeer) { nodeID0 := ids.NodeIDFromCert(tlsCert0.Leaf) nodeID1 := ids.NodeIDFromCert(tlsCert1.Leaf) - mc := newMessageCreator(t) - - pingMessage, err := mc.Ping() - require.NoError(err) + mc, mcProto := newMessageCreator(t) metrics, err := NewMetrics( logging.NoLog{}, @@ -85,19 +93,19 @@ func makeRawTestPeers(t *testing.T) (*rawTestPeer, *rawTestPeer) { resourceTracker, err := tracker.NewResourceTracker(prometheus.NewRegistry(), resource.NoUsage, meter.ContinuousFactory{}, 10*time.Second) require.NoError(err) sharedConfig := Config{ - Metrics: metrics, - MessageCreator: mc, - Log: logging.NoLog{}, - InboundMsgThrottler: throttling.NewNoInboundThrottler(), - VersionCompatibility: version.GetCompatibility(constants.LocalID), - MySubnets: ids.Set{}, - Beacons: validators.NewSet(), - NetworkID: constants.LocalID, - PingFrequency: constants.DefaultPingFrequency, - PongTimeout: constants.DefaultPingPongTimeout, - MaxClockDifference: time.Minute, - ResourceTracker: resourceTracker, - PingMessage: pingMessage, + Metrics: metrics, + MessageCreator: mc, + MessageCreatorWithProto: mcProto, + Log: logging.NoLog{}, + InboundMsgThrottler: throttling.NewNoInboundThrottler(), + VersionCompatibility: version.GetCompatibility(constants.LocalID), + MySubnets: ids.Set{}, + Beacons: validators.NewSet(), + NetworkID: constants.LocalID, + PingFrequency: constants.DefaultPingFrequency, + PongTimeout: constants.DefaultPingPongTimeout, + MaxClockDifference: time.Minute, + ResourceTracker: resourceTracker, } peerConfig0 := sharedConfig peerConfig1 := sharedConfig @@ -215,72 +223,82 @@ func makeReadyTestPeers(t *testing.T) (*testPeer, *testPeer) { func TestReady(t *testing.T) { require := require.New(t) - rawPeer0, rawPeer1 := makeRawTestPeers(t) - - peer0 := Start( - rawPeer0.config, - rawPeer0.conn, - rawPeer1.cert, - rawPeer1.nodeID, - NewThrottledMessageQueue( - rawPeer0.config.Metrics, - rawPeer1.nodeID, - logging.NoLog{}, - throttling.NewNoOutboundThrottler(), - ), - ) + // TODO: once "NewNetwork" handles proto, add "true" + for _, useProto := range []bool{false} { + t.Run(fmt.Sprintf("use proto buf message creator %v", useProto), func(tt *testing.T) { + rawPeer0, rawPeer1 := makeRawTestPeers(tt) - isReady := peer0.Ready() - require.False(isReady) - - peer1 := Start( - rawPeer1.config, - rawPeer1.conn, - rawPeer0.cert, - rawPeer0.nodeID, - NewThrottledMessageQueue( - rawPeer1.config.Metrics, - rawPeer0.nodeID, - logging.NoLog{}, - throttling.NewNoOutboundThrottler(), - ), - ) - - err := peer0.AwaitReady(context.Background()) - require.NoError(err) - isReady = peer0.Ready() - require.True(isReady) - - err = peer1.AwaitReady(context.Background()) - require.NoError(err) - isReady = peer1.Ready() - require.True(isReady) - - peer0.StartClose() - err = peer0.AwaitClosed(context.Background()) - require.NoError(err) - err = peer1.AwaitClosed(context.Background()) - require.NoError(err) + peer0 := Start( + rawPeer0.config, + rawPeer0.conn, + rawPeer1.cert, + rawPeer1.nodeID, + NewThrottledMessageQueue( + rawPeer0.config.Metrics, + rawPeer1.nodeID, + logging.NoLog{}, + throttling.NewNoOutboundThrottler(), + ), + ) + + isReady := peer0.Ready() + require.False(isReady) + + peer1 := Start( + rawPeer1.config, + rawPeer1.conn, + rawPeer0.cert, + rawPeer0.nodeID, + NewThrottledMessageQueue( + rawPeer1.config.Metrics, + rawPeer0.nodeID, + logging.NoLog{}, + throttling.NewNoOutboundThrottler(), + ), + ) + + err := peer0.AwaitReady(context.Background()) + require.NoError(err) + isReady = peer0.Ready() + require.True(isReady) + + err = peer1.AwaitReady(context.Background()) + require.NoError(err) + isReady = peer1.Ready() + require.True(isReady) + + peer0.StartClose() + err = peer0.AwaitClosed(context.Background()) + require.NoError(err) + err = peer1.AwaitClosed(context.Background()) + require.NoError(err) + }) + } } func TestSend(t *testing.T) { require := require.New(t) - peer0, peer1 := makeReadyTestPeers(t) - mc := newMessageCreator(t) + // TODO: add "true" to test proto + for _, useProto := range []bool{false} { + t.Run(fmt.Sprintf("use proto buf message creator %v", useProto), func(tt *testing.T) { + peer0, peer1 := makeReadyTestPeers(tt) + mc, _ := newMessageCreator(t) - outboundGetMsg, err := mc.Get(ids.Empty, 1, time.Second, ids.Empty) - require.NoError(err) + outboundGetMsg, err := mc.Get(ids.Empty, 1, time.Second, ids.Empty) + require.NoError(err) - sent := peer0.Send(context.Background(), outboundGetMsg) - require.True(sent) + sent := peer0.Send(context.Background(), outboundGetMsg) + require.True(sent) - inboundGetMsg := <-peer1.inboundMsgChan - require.Equal(message.Get, inboundGetMsg.Op()) + inboundGetMsg := <-peer1.inboundMsgChan + require.Equal(message.Get, inboundGetMsg.Op()) - peer1.StartClose() - err = peer0.AwaitClosed(context.Background()) - require.NoError(err) - err = peer1.AwaitClosed(context.Background()) - require.NoError(err) + peer1.StartClose() + err = peer0.AwaitClosed(context.Background()) + require.NoError(err) + err = peer1.AwaitClosed(context.Background()) + require.NoError(err) + }) + } } diff --git a/avalanchego/network/peer/set.go b/avalanchego/network/peer/set.go index 6b4e374d..c3c3904e 100644 --- a/avalanchego/network/peer/set.go +++ b/avalanchego/network/peer/set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer @@ -37,7 +37,7 @@ type Set interface { Len() int // Sample attempts to return a random slice of peers with length [n]. The - // slice will not inclide any duplicates. Only peers that cause the + // slice will not include any duplicates. Only peers that cause the // [precondition] to return true will be returned in the slice. Sample(n int, precondition func(Peer) bool) []Peer diff --git a/avalanchego/network/peer/set_test.go b/avalanchego/network/peer/set_test.go index 88fe4531..705a96ac 100644 --- a/avalanchego/network/peer/set_test.go +++ b/avalanchego/network/peer/set_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/test_network.go b/avalanchego/network/peer/test_network.go index af26e8ee..c55b1f00 100644 --- a/avalanchego/network/peer/test_network.go +++ b/avalanchego/network/peer/test_network.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/peer/test_peer.go b/avalanchego/network/peer/test_peer.go index 30720b0d..7f45165d 100644 --- a/avalanchego/network/peer/test_peer.go +++ b/avalanchego/network/peer/test_peer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer @@ -35,13 +35,13 @@ const maxMessageToSend = 1024 // // The returned peer will not throttle inbound or outbound messages. // -// - [ctx] provides a way of canceling the connection request. -// - [ip] is the remote that will be dialed to create the connection. -// - [networkID] will be sent to the peer during the handshake. If the peer is -// expecting a different [networkID], the handshake will fail and an error -// will be returned. -// - [router] will be called with all non-handshake messages received by the -// peer. +// - [ctx] provides a way of canceling the connection request. +// - [ip] is the remote that will be dialed to create the connection. +// - [networkID] will be sent to the peer during the handshake. If the peer is +// expecting a different [networkID], the handshake will fail and an error +// will be returned. +// - [router] will be called with all non-handshake messages received by the +// peer. func StartTestPeer( ctx context.Context, ip ips.IPPort, @@ -59,7 +59,7 @@ func StartTestPeer( return nil, err } - tlsConfg := TLSConfig(*tlsCert) + tlsConfg := TLSConfig(*tlsCert, nil) clientUpgrader := NewTLSClientUpgrader(tlsConfg) peerID, conn, cert, err := clientUpgrader.Upgrade(conn) @@ -69,8 +69,18 @@ func StartTestPeer( mc, err := message.NewCreator( prometheus.NewRegistry(), + "", true, + 10*time.Second, + ) + if err != nil { + return nil, err + } + + mcWithProto, err := message.NewCreatorWithProto( + prometheus.NewRegistry(), "", + true, 10*time.Second, ) if err != nil { @@ -95,17 +105,14 @@ func StartTestPeer( return nil, err } - pingMessage, err := mc.Ping() - if err != nil { - return nil, err - } - peer := Start( &Config{ - Metrics: metrics, - MessageCreator: mc, - Log: logging.NoLog{}, - InboundMsgThrottler: throttling.NewNoInboundThrottler(), + Metrics: metrics, + MessageCreator: mc, + MessageCreatorWithProto: mcWithProto, + BanffTime: version.GetBanffTime(networkID), + Log: logging.NoLog{}, + InboundMsgThrottler: throttling.NewNoInboundThrottler(), Network: NewTestNetwork( mc, networkID, @@ -124,7 +131,6 @@ func StartTestPeer( PongTimeout: constants.DefaultPingPongTimeout, MaxClockDifference: time.Minute, ResourceTracker: resourceTracker, - PingMessage: pingMessage, }, conn, cert, diff --git a/avalanchego/network/peer/tls_config.go b/avalanchego/network/peer/tls_config.go index 1971ac84..06437909 100644 --- a/avalanchego/network/peer/tls_config.go +++ b/avalanchego/network/peer/tls_config.go @@ -1,11 +1,19 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer -import "crypto/tls" +import ( + "crypto/tls" + "io" +) -func TLSConfig(cert tls.Certificate) *tls.Config { +// TLSConfig returns the TLS config that will allow secure connections to other +// peers. +// +// It is safe, and typically expected, for [keyLogWriter] to be [nil]. +// [keyLogWriter] should only be enabled for debugging. +func TLSConfig(cert tls.Certificate, keyLogWriter io.Writer) *tls.Config { // #nosec G402 return &tls.Config{ Certificates: []tls.Certificate{cert}, @@ -18,5 +26,6 @@ func TLSConfig(cert tls.Certificate) *tls.Config { // and confirmed to be safe and correct. InsecureSkipVerify: true, MinVersion: tls.VersionTLS13, + KeyLogWriter: keyLogWriter, } } diff --git a/avalanchego/network/peer/upgrader.go b/avalanchego/network/peer/upgrader.go index e718ad3b..3428e587 100644 --- a/avalanchego/network/peer/upgrader.go +++ b/avalanchego/network/peer/upgrader.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package peer diff --git a/avalanchego/network/throttling/bandwidth_throttler.go b/avalanchego/network/throttling/bandwidth_throttler.go index 8d123bd9..f7d2dfa8 100644 --- a/avalanchego/network/throttling/bandwidth_throttler.go +++ b/avalanchego/network/throttling/bandwidth_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/bandwidth_throttler_test.go b/avalanchego/network/throttling/bandwidth_throttler_test.go index 8a447df5..318d767e 100644 --- a/avalanchego/network/throttling/bandwidth_throttler_test.go +++ b/avalanchego/network/throttling/bandwidth_throttler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/common.go b/avalanchego/network/throttling/common.go index 2430323e..4cb8d50c 100644 --- a/avalanchego/network/throttling/common.go +++ b/avalanchego/network/throttling/common.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/dial_throttler.go b/avalanchego/network/throttling/dial_throttler.go index 44239182..7740fca5 100644 --- a/avalanchego/network/throttling/dial_throttler.go +++ b/avalanchego/network/throttling/dial_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/dial_throttler_test.go b/avalanchego/network/throttling/dial_throttler_test.go index 5dc0cd74..30d6caa8 100644 --- a/avalanchego/network/throttling/dial_throttler_test.go +++ b/avalanchego/network/throttling/dial_throttler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/inbound_conn_throttler.go b/avalanchego/network/throttling/inbound_conn_throttler.go index 47550b91..ec8decc1 100644 --- a/avalanchego/network/throttling/inbound_conn_throttler.go +++ b/avalanchego/network/throttling/inbound_conn_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/inbound_conn_throttler_test.go b/avalanchego/network/throttling/inbound_conn_throttler_test.go index cbc91609..e3d7b2d7 100644 --- a/avalanchego/network/throttling/inbound_conn_throttler_test.go +++ b/avalanchego/network/throttling/inbound_conn_throttler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/inbound_conn_upgrade_throttler.go b/avalanchego/network/throttling/inbound_conn_upgrade_throttler.go index 1d23715e..2150eb83 100644 --- a/avalanchego/network/throttling/inbound_conn_upgrade_throttler.go +++ b/avalanchego/network/throttling/inbound_conn_upgrade_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/inbound_conn_upgrade_throttler_test.go b/avalanchego/network/throttling/inbound_conn_upgrade_throttler_test.go index 3c19aec2..e0cb5f5f 100644 --- a/avalanchego/network/throttling/inbound_conn_upgrade_throttler_test.go +++ b/avalanchego/network/throttling/inbound_conn_upgrade_throttler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/inbound_msg_buffer_throttler.go b/avalanchego/network/throttling/inbound_msg_buffer_throttler.go index 740f24cf..1760a9e0 100644 --- a/avalanchego/network/throttling/inbound_msg_buffer_throttler.go +++ b/avalanchego/network/throttling/inbound_msg_buffer_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/inbound_msg_buffer_throttler_test.go b/avalanchego/network/throttling/inbound_msg_buffer_throttler_test.go index 53a38fd6..5ead7d46 100644 --- a/avalanchego/network/throttling/inbound_msg_buffer_throttler_test.go +++ b/avalanchego/network/throttling/inbound_msg_buffer_throttler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/inbound_msg_byte_throttler.go b/avalanchego/network/throttling/inbound_msg_byte_throttler.go index 482e4ea0..ec7334d3 100644 --- a/avalanchego/network/throttling/inbound_msg_byte_throttler.go +++ b/avalanchego/network/throttling/inbound_msg_byte_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling @@ -40,7 +40,7 @@ func newInboundMsgByteThrottler( nodeToVdrBytesUsed: make(map[ids.NodeID]uint64), nodeToAtLargeBytesUsed: make(map[ids.NodeID]uint64), }, - waitingToAcquire: linkedhashmap.New(), + waitingToAcquire: linkedhashmap.New[uint64, *msgMetadata](), nodeToWaitingMsgID: make(map[ids.NodeID]uint64), } return t, t.metrics.initialize(namespace, registerer) @@ -68,7 +68,7 @@ type inboundMsgByteThrottler struct { // Node ID --> Msg ID for a message this node is waiting to acquire nodeToWaitingMsgID map[ids.NodeID]uint64 // Msg ID --> *msgMetadata - waitingToAcquire linkedhashmap.LinkedHashmap + waitingToAcquire linkedhashmap.LinkedHashmap[uint64, *msgMetadata] // Invariant: The node is only waiting on a single message at a time // // Invariant: waitingToAcquire.Get(nodeToWaitingMsgIDs[nodeID]) @@ -224,7 +224,7 @@ func (t *inboundMsgByteThrottler) release(metadata *msgMetadata, nodeID ids.Node // waiting messages or we exhaust the bytes. iter := t.waitingToAcquire.NewIterator() for t.remainingAtLargeBytes > 0 && iter.Next() { - msg := iter.Value().(*msgMetadata) + msg := iter.Value() // From the at-large allocation, take the maximum number of bytes // without exceeding the per-node limit on taking from at-large pool. atLargeBytesGiven := math.Min64( @@ -257,10 +257,9 @@ func (t *inboundMsgByteThrottler) release(metadata *msgMetadata, nodeID ids.Node // Get the message from [nodeID], if any, waiting to acquire msgID, ok := t.nodeToWaitingMsgID[nodeID] if vdrBytesToReturn > 0 && ok { - msgIntf, exists := t.waitingToAcquire.Get(msgID) + msg, exists := t.waitingToAcquire.Get(msgID) if exists { // Give [msg] all the bytes we can - msg := msgIntf.(*msgMetadata) bytesToGive := math.Min64(msg.bytesNeeded, vdrBytesToReturn) msg.bytesNeeded -= bytesToGive vdrBytesToReturn -= bytesToGive diff --git a/avalanchego/network/throttling/inbound_msg_byte_throttler_test.go b/avalanchego/network/throttling/inbound_msg_byte_throttler_test.go index c22b1d04..13aa7f98 100644 --- a/avalanchego/network/throttling/inbound_msg_byte_throttler_test.go +++ b/avalanchego/network/throttling/inbound_msg_byte_throttler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling @@ -428,7 +428,7 @@ func TestMsgThrottlerNextMsg(t *testing.T) { firstMsgID := throttler.nodeToWaitingMsgID[vdr1ID] firstMsg, exists := throttler.waitingToAcquire.Get(firstMsgID) require.True(exists) - require.EqualValues(maxBytes-2, firstMsg.(*msgMetadata).bytesNeeded) + require.EqualValues(maxBytes-2, firstMsg.bytesNeeded) select { case <-doneVdr: diff --git a/avalanchego/network/throttling/inbound_msg_throttler.go b/avalanchego/network/throttling/inbound_msg_throttler.go index 1888f672..fded1b3a 100644 --- a/avalanchego/network/throttling/inbound_msg_throttler.go +++ b/avalanchego/network/throttling/inbound_msg_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/no_inbound_msg_throttler.go b/avalanchego/network/throttling/no_inbound_msg_throttler.go index 7ad22739..046b2d24 100644 --- a/avalanchego/network/throttling/no_inbound_msg_throttler.go +++ b/avalanchego/network/throttling/no_inbound_msg_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/outbound_msg_throttler.go b/avalanchego/network/throttling/outbound_msg_throttler.go index ecaff3e9..afa69371 100644 --- a/avalanchego/network/throttling/outbound_msg_throttler.go +++ b/avalanchego/network/throttling/outbound_msg_throttler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/outbound_msg_throttler_test.go b/avalanchego/network/throttling/outbound_msg_throttler_test.go index 8cbac67c..9729a873 100644 --- a/avalanchego/network/throttling/outbound_msg_throttler_test.go +++ b/avalanchego/network/throttling/outbound_msg_throttler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/throttling/release_func.go b/avalanchego/network/throttling/release_func.go index 5b23378e..3ea58391 100644 --- a/avalanchego/network/throttling/release_func.go +++ b/avalanchego/network/throttling/release_func.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package throttling diff --git a/avalanchego/network/tracked_ip.go b/avalanchego/network/tracked_ip.go index 643ceefd..93630b46 100644 --- a/avalanchego/network/tracked_ip.go +++ b/avalanchego/network/tracked_ip.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/network/tracked_ip_test.go b/avalanchego/network/tracked_ip_test.go index 9cc446be..f51cea37 100644 --- a/avalanchego/network/tracked_ip_test.go +++ b/avalanchego/network/tracked_ip_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package network diff --git a/avalanchego/node/config.go b/avalanchego/node/config.go index 7f6235f4..3c90dd35 100644 --- a/avalanchego/node/config.go +++ b/avalanchego/node/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package node @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanchego/snow/networking/router" "github.com/ava-labs/avalanchego/snow/networking/sender" "github.com/ava-labs/avalanchego/snow/networking/tracker" + "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/dynamicip" "github.com/ava-labs/avalanchego/utils/ips" "github.com/ava-labs/avalanchego/utils/logging" @@ -83,9 +84,11 @@ type StakingConfig struct { genesis.StakingConfig EnableStaking bool `json:"enableStaking"` StakingTLSCert tls.Certificate `json:"-"` + StakingSigningKey *bls.SecretKey `json:"-"` DisabledStakingWeight uint64 `json:"disabledStakingWeight"` StakingKeyPath string `json:"stakingKeyPath"` StakingCertPath string `json:"stakingCertPath"` + StakingSignerPath string `json:"stakingSignerPath"` } type StateSyncConfig struct { @@ -100,7 +103,7 @@ type BootstrapConfig struct { // Max number of times to retry bootstrap before warning the node operator RetryBootstrapWarnFrequency int `json:"retryBootstrapWarnFrequency"` - // Timeout when connecting to bootstrapping beacons + // Timeout before emitting a warn log when connecting to bootstrapping beacons BootstrapBeaconConnectionTimeout time.Duration `json:"bootstrapBeaconConnectionTimeout"` // Max number of containers in an ancestors message sent by this node. @@ -146,12 +149,6 @@ type Config struct { // ID of the network this node should connect to NetworkID uint32 `json:"networkID"` - // Assertions configuration - EnableAssertions bool `json:"enableAssertions"` - - // Crypto configuration - EnableCrypto bool `json:"enableCrypto"` - // Health HealthCheckFreq time.Duration `json:"healthCheckFreq"` diff --git a/avalanchego/node/node.go b/avalanchego/node/node.go index 33690f1c..8bebff97 100644 --- a/avalanchego/node/node.go +++ b/avalanchego/node/node.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package node @@ -7,6 +7,7 @@ import ( "crypto" "errors" "fmt" + "io" "net" "os" "path/filepath" @@ -62,6 +63,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/math/meter" + "github.com/ava-labs/avalanchego/utils/perms" "github.com/ava-labs/avalanchego/utils/profiler" "github.com/ava-labs/avalanchego/utils/resource" "github.com/ava-labs/avalanchego/utils/timer" @@ -71,6 +73,7 @@ import ( "github.com/ava-labs/avalanchego/vms/nftfx" "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/avalanchego/vms/platformvm/config" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/propertyfx" "github.com/ava-labs/avalanchego/vms/registry" "github.com/ava-labs/avalanchego/vms/secp256k1fx" @@ -115,7 +118,8 @@ type Node struct { health health.Health // Build and parse messages, for both network layer and chain manager - msgCreator message.Creator + msgCreator message.Creator + msgCreatorWithProto message.Creator // Manages creation of blockchains and routing messages to them chainManager chains.Manager @@ -135,6 +139,10 @@ type Node struct { networkNamespace string Net network.Network + // tlsKeyLogWriterCloser is a debug file handle that writes all the TLS + // session keys. This value should only be non-nil during debugging. + tlsKeyLogWriterCloser io.WriteCloser + // this node's initial connections to the network beacons validators.Set @@ -219,7 +227,17 @@ func (n *Node) initNetworking(primaryNetVdrs validators.Set) error { return errInvalidTLSKey } - tlsConfig := peer.TLSConfig(n.Config.StakingTLSCert) + if n.Config.NetworkConfig.TLSKeyLogFile != "" { + n.tlsKeyLogWriterCloser, err = perms.Create(n.Config.NetworkConfig.TLSKeyLogFile, perms.ReadWrite) + if err != nil { + return err + } + n.Log.Warn("TLS key logging is enabled", + zap.String("filename", n.Config.NetworkConfig.TLSKeyLogFile), + ) + } + + tlsConfig := peer.TLSConfig(n.Config.StakingTLSCert, n.tlsKeyLogWriterCloser) // Configure benchlist n.Config.BenchlistConfig.Validators = n.vdrs @@ -251,11 +269,10 @@ func (n *Node) initNetworking(primaryNetVdrs validators.Set) error { timer := timer.NewTimer(func() { // If the timeout fires and we're already shutting down, nothing to do. if !n.shuttingDown.GetValue() { - n.Log.Debug("failed to connect to bootstrap nodes in time", + n.Log.Warn("failed to connect to bootstrap nodes", zap.Stringer("beacons", n.beacons), + zap.Duration("duration", n.Config.BootstrapBeaconConnectionTimeout), ) - n.Log.Fatal("failed to connect to bootstrap nodes. Node shutting down...") - go n.Shutdown(1) } }) @@ -289,6 +306,8 @@ func (n *Node) initNetworking(primaryNetVdrs validators.Set) error { n.Net, err = network.NewNetwork( &n.Config.NetworkConfig, n.msgCreator, + n.msgCreatorWithProto, + version.GetBanffTime(n.Config.NetworkID), n.MetricsRegisterer, n.Log, listener, @@ -405,6 +424,16 @@ func (n *Node) Dispatch() error { // If node is already shutting down, this does nothing. n.Shutdown(1) + if n.tlsKeyLogWriterCloser != nil { + err := n.tlsKeyLogWriterCloser.Close() + if err != nil { + n.Log.Error("closing TLS key log file failed", + zap.String("filename", n.Config.NetworkConfig.TLSKeyLogFile), + zap.Error(err), + ) + } + } + // Wait until the node is done shutting down before returning n.DoneShuttingDown.Wait() return err @@ -679,6 +708,7 @@ func (n *Node) initChainManager(avaxAssetID ids.ID) error { ConsensusAcceptorGroup: n.ConsensusAcceptorGroup, DBManager: n.DBManager, MsgCreator: n.msgCreator, + MsgCreatorWithProto: n.msgCreatorWithProto, Router: n.Config.ConsensusRouter, Net: n.Net, ConsensusParams: n.Config.ConsensusParams, @@ -708,6 +738,7 @@ func (n *Node) initChainManager(avaxAssetID ids.ID) error { BootstrapAncestorsMaxContainersReceived: n.Config.BootstrapAncestorsMaxContainersReceived, ApricotPhase4Time: version.GetApricotPhase4Time(n.Config.NetworkID), ApricotPhase4MinPChainHeight: version.GetApricotPhase4MinPChainHeight(n.Config.NetworkID), + BanffTime: version.GetBanffTime(n.Config.NetworkID), ResourceTracker: n.resourceTracker, StateSyncBeacons: n.Config.StateSyncIDs, }) @@ -741,33 +772,38 @@ func (n *Node) initVMs() error { errs.Add( vmRegisterer.Register(constants.PlatformVMID, &platformvm.Factory{ Config: config.Config{ - Chains: n.chainManager, - Validators: vdrs, - SubnetTracker: n.Net, - UptimeLockedCalculator: n.uptimeCalculator, - StakingEnabled: n.Config.EnableStaking, - WhitelistedSubnets: n.Config.WhitelistedSubnets, - TxFee: n.Config.TxFee, - CreateAssetTxFee: n.Config.CreateAssetTxFee, - CreateSubnetTxFee: n.Config.CreateSubnetTxFee, - CreateBlockchainTxFee: n.Config.CreateBlockchainTxFee, - UptimePercentage: n.Config.UptimeRequirement, - MinValidatorStake: n.Config.MinValidatorStake, - MaxValidatorStake: n.Config.MaxValidatorStake, - MinDelegatorStake: n.Config.MinDelegatorStake, - MinDelegationFee: n.Config.MinDelegationFee, - MinStakeDuration: n.Config.MinStakeDuration, - MaxStakeDuration: n.Config.MaxStakeDuration, - RewardConfig: n.Config.RewardConfig, - ApricotPhase3Time: version.GetApricotPhase3Time(n.Config.NetworkID), - ApricotPhase5Time: version.GetApricotPhase5Time(n.Config.NetworkID), - BlueberryTime: version.GetBlueberryTime(n.Config.NetworkID), + Chains: n.chainManager, + Validators: vdrs, + SubnetTracker: n.Net, + UptimeLockedCalculator: n.uptimeCalculator, + StakingEnabled: n.Config.EnableStaking, + WhitelistedSubnets: n.Config.WhitelistedSubnets, + TxFee: n.Config.TxFee, + CreateAssetTxFee: n.Config.CreateAssetTxFee, + CreateSubnetTxFee: n.Config.CreateSubnetTxFee, + TransformSubnetTxFee: n.Config.TransformSubnetTxFee, + CreateBlockchainTxFee: n.Config.CreateBlockchainTxFee, + AddPrimaryNetworkValidatorFee: n.Config.AddPrimaryNetworkValidatorFee, + AddPrimaryNetworkDelegatorFee: n.Config.AddPrimaryNetworkDelegatorFee, + AddSubnetValidatorFee: n.Config.AddSubnetValidatorFee, + AddSubnetDelegatorFee: n.Config.AddSubnetDelegatorFee, + UptimePercentage: n.Config.UptimeRequirement, + MinValidatorStake: n.Config.MinValidatorStake, + MaxValidatorStake: n.Config.MaxValidatorStake, + MinDelegatorStake: n.Config.MinDelegatorStake, + MinDelegationFee: n.Config.MinDelegationFee, + MinStakeDuration: n.Config.MinStakeDuration, + MaxStakeDuration: n.Config.MaxStakeDuration, + RewardConfig: n.Config.RewardConfig, + ApricotPhase3Time: version.GetApricotPhase3Time(n.Config.NetworkID), + ApricotPhase5Time: version.GetApricotPhase5Time(n.Config.NetworkID), + BanffTime: version.GetBanffTime(n.Config.NetworkID), }, }), vmRegisterer.Register(constants.AVMID, &avm.Factory{ TxFee: n.Config.TxFee, CreateAssetTxFee: n.Config.CreateAssetTxFee, - BlueberryTime: version.GetBlueberryTime(n.Config.NetworkID), + BanffTime: version.GetBanffTime(n.Config.NetworkID), }), vmRegisterer.Register(constants.EVMID, &coreth.Factory{}), n.Config.VMManager.RegisterFactory(secp256k1fx.ID, &secp256k1fx.Factory{}), @@ -933,14 +969,20 @@ func (n *Node) initInfoAPI() error { primaryValidators, _ := n.vdrs.GetValidators(constants.PrimaryNetworkID) service, err := info.NewService( info.Parameters{ - Version: version.CurrentApp, - NodeID: n.ID, - NetworkID: n.Config.NetworkID, - TxFee: n.Config.TxFee, - CreateAssetTxFee: n.Config.CreateAssetTxFee, - CreateSubnetTxFee: n.Config.CreateSubnetTxFee, - CreateBlockchainTxFee: n.Config.CreateBlockchainTxFee, - VMManager: n.Config.VMManager, + Version: version.CurrentApp, + NodeID: n.ID, + NodePOP: signer.NewProofOfPossession(n.Config.StakingSigningKey), + NetworkID: n.Config.NetworkID, + TxFee: n.Config.TxFee, + CreateAssetTxFee: n.Config.CreateAssetTxFee, + CreateSubnetTxFee: n.Config.CreateSubnetTxFee, + TransformSubnetTxFee: n.Config.TransformSubnetTxFee, + CreateBlockchainTxFee: n.Config.CreateBlockchainTxFee, + AddPrimaryNetworkValidatorFee: n.Config.AddPrimaryNetworkValidatorFee, + AddPrimaryNetworkDelegatorFee: n.Config.AddPrimaryNetworkDelegatorFee, + AddSubnetValidatorFee: n.Config.AddSubnetValidatorFee, + AddSubnetDelegatorFee: n.Config.AddSubnetDelegatorFee, + VMManager: n.Config.VMManager, }, n.Log, n.chainManager, @@ -1182,9 +1224,11 @@ func (n *Node) Initialize( n.LogFactory = logFactory n.DoneShuttingDown.Add(1) + pop := signer.NewProofOfPossession(n.Config.StakingSigningKey) n.Log.Info("initializing node", zap.Stringer("version", version.CurrentApp), zap.Stringer("nodeID", n.ID), + zap.Reflect("nodePOP", pop), zap.Reflect("config", n.Config), ) @@ -1215,14 +1259,24 @@ func (n *Node) Initialize( // and the engine (initChains) but after the metrics (initMetricsAPI) // message.Creator currently record metrics under network namespace n.networkNamespace = "network" - n.msgCreator, err = message.NewCreator(n.MetricsRegisterer, - n.Config.NetworkConfig.CompressionEnabled, + n.msgCreator, err = message.NewCreator( + n.MetricsRegisterer, n.networkNamespace, + n.Config.NetworkConfig.CompressionEnabled, n.Config.NetworkConfig.MaximumInboundMessageTimeout, ) if err != nil { return fmt.Errorf("problem initializing message creator: %w", err) } + n.msgCreatorWithProto, err = message.NewCreatorWithProto( + n.MetricsRegisterer, + n.networkNamespace, + n.Config.NetworkConfig.CompressionEnabled, + n.Config.NetworkConfig.MaximumInboundMessageTimeout, + ) + if err != nil { + return fmt.Errorf("problem initializing message creator with proto: %w", err) + } primaryNetVdrs, err := n.initVdrs() if err != nil { diff --git a/avalanchego/proto/Dockerfile.buf b/avalanchego/proto/Dockerfile.buf index 31740880..b95f4998 100644 --- a/avalanchego/proto/Dockerfile.buf +++ b/avalanchego/proto/Dockerfile.buf @@ -1,4 +1,4 @@ -FROM bufbuild/buf:1.5.0 AS builder +FROM bufbuild/buf:1.7.0 AS builder FROM ubuntu:20.04 diff --git a/avalanchego/proto/README.md b/avalanchego/proto/README.md index fd003bf2..b307e072 100644 --- a/avalanchego/proto/README.md +++ b/avalanchego/proto/README.md @@ -1,12 +1,18 @@ # Avalanche gRPC -## buf -Protobuf files are hosted at [https://buf.build/ava-labs/avalanchego](https://buf.build/ava-labs/avalanchego) and can be used as dependencies in other projects. +Now Serving: **Protocol Version 16** + +Protobuf files are hosted at [https://buf.build/ava-labs/avalanche](https://buf.build/ava-labs/avalanche) and can be used as dependencies in other projects. Protobuf linting and generation for this project is managed by [buf](https://github.com/bufbuild/buf). -Please find installation instructions on [https://docs.buf.build/installation/](https://docs.buf.build/installation/) or use the provided `Dockerfile.buf`. +Please find installation instructions on [https://docs.buf.build/installation/](https://docs.buf.build/installation/) or use `Dockerfile.buf` provided in the `proto/` directory of AvalancheGo. -Any changes made to proto files can be updated by running `protobuf_codegen.sh` located in the `scripts/` directory. +Any changes made to proto definition can be updated by running `protobuf_codegen.sh` located in the `scripts/` directory of AvalancheGo. Introduction to `buf` [https://docs.buf.build/tour/introduction](https://docs.buf.build/tour/introduction) + +## Protocol Version Compatibility + +The protobuf definitions and generated code are versioned based on the [protocolVersion](../vms/rpcchainvm/vm.go#L21) defined by the rpcchainvm. +Many versions of an Avalanche client can use the same [protocolVersion](../vms/rpcchainvm/vm.go#L21). But each Avalanche client and subnet vm must use the same protocol version to be compatible. diff --git a/avalanchego/proto/aliasreader/aliasreader.proto b/avalanchego/proto/aliasreader/aliasreader.proto index 970ad597..926f86b1 100644 --- a/avalanchego/proto/aliasreader/aliasreader.proto +++ b/avalanchego/proto/aliasreader/aliasreader.proto @@ -4,6 +4,12 @@ package aliasreader; option go_package = "github.com/ava-labs/avalanchego/proto/pb/aliasreader"; +service AliasReader { + rpc Lookup(Alias) returns (ID); + rpc PrimaryAlias(ID) returns (Alias); + rpc Aliases(ID) returns (AliasList); +} + message ID { bytes id = 1; } @@ -15,9 +21,3 @@ message Alias { message AliasList { repeated string aliases = 1; } - -service AliasReader { - rpc Lookup(Alias) returns (ID); - rpc PrimaryAlias(ID) returns (Alias); - rpc Aliases(ID) returns (AliasList); -} diff --git a/avalanchego/proto/appsender/appsender.proto b/avalanchego/proto/appsender/appsender.proto index 7362616b..ee98356a 100644 --- a/avalanchego/proto/appsender/appsender.proto +++ b/avalanchego/proto/appsender/appsender.proto @@ -6,6 +6,13 @@ import "google/protobuf/empty.proto"; option go_package = "github.com/ava-labs/avalanchego/proto/pb/appsender"; +service AppSender { + rpc SendAppRequest(SendAppRequestMsg) returns (google.protobuf.Empty); + rpc SendAppResponse(SendAppResponseMsg) returns (google.protobuf.Empty); + rpc SendAppGossip(SendAppGossipMsg) returns (google.protobuf.Empty); + rpc SendAppGossipSpecific(SendAppGossipSpecificMsg) returns (google.protobuf.Empty); +} + message SendAppRequestMsg { // The nodes to send this request to repeated bytes node_ids = 1; @@ -35,10 +42,3 @@ message SendAppGossipSpecificMsg { // The message body bytes msg = 2; } - -service AppSender { - rpc SendAppRequest(SendAppRequestMsg) returns (google.protobuf.Empty); - rpc SendAppResponse(SendAppResponseMsg) returns (google.protobuf.Empty); - rpc SendAppGossip(SendAppGossipMsg) returns (google.protobuf.Empty); - rpc SendAppGossipSpecific(SendAppGossipSpecificMsg) returns (google.protobuf.Empty); -} diff --git a/avalanchego/proto/buf.md b/avalanchego/proto/buf.md new file mode 100644 index 00000000..42061c01 --- /dev/null +++ b/avalanchego/proto/buf.md @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/avalanchego/proto/buf.yaml b/avalanchego/proto/buf.yaml index 109f0eac..52df7ce5 100644 --- a/avalanchego/proto/buf.yaml +++ b/avalanchego/proto/buf.yaml @@ -1,5 +1,5 @@ version: v1 -name: buf.build/ava-labs/avalanchego +name: buf.build/ava-labs/avalanche build: excludes: # for golang we handle prometheus as a buf dep so we exclude it from generate, this proto diff --git a/avalanchego/proto/http/http.proto b/avalanchego/proto/http/http.proto index 7eb303a6..bb281cb6 100644 --- a/avalanchego/proto/http/http.proto +++ b/avalanchego/proto/http/http.proto @@ -63,8 +63,6 @@ message Certificates { // ConnectionState is tls.ConnectionState see: https://pkg.go.dev/crypto/tls#ConnectionState message ConnectionState { - // reserved fields ids have been removed and should not be reused - reserved 6, 12; // version is the TLS version used by the connection (e.g. VersionTLS12) uint32 version = 1; // handshake_complete is true if the handshake has concluded @@ -78,20 +76,20 @@ message ConnectionState { string negotiated_protocol = 5; // server_name is the value of the Server Name Indication extension sent by // the client - string server_name = 7; + string server_name = 6; // peer_certificates are the parsed certificates sent by the peer, in the // order in which they were sent - Certificates peer_certificates = 8; + Certificates peer_certificates = 7; // verified_chains is a list of one or more chains where the first element is // PeerCertificates[0] and the last element is from Config.RootCAs (on the // client side) or Config.ClientCAs (on the server side). - repeated Certificates verified_chains = 9; + repeated Certificates verified_chains = 8; // signed_certificate_timestamps is a list of SCTs provided by the peer // through the TLS handshake for the leaf certificate, if any - repeated bytes signed_certificate_timestamps = 10; + repeated bytes signed_certificate_timestamps = 9; // ocsp_response is a stapled Online Certificate Status Protocol (OCSP) // response provided by the peer for the leaf certificate, if any. - bytes ocsp_response = 11; + bytes ocsp_response = 10; } // Request is an http.Request see: https://pkg.go.dev/net/http#Request @@ -137,15 +135,11 @@ message Request { } message ResponseWriter { - // reserved for backward compatibility - // avalanchego <=v1.7.9 used the field "1" as an id to identify the gRPC server - // address which served the Writer service via the now removed service broker - reserved 1; // header returns the header map that will be sent by // WriteHeader. - repeated Element header = 2; + repeated Element header = 1; // server_addr is the address of the gRPC server hosting the Writer service - string server_addr = 3; + string server_addr = 2; } message HTTPRequest { diff --git a/avalanchego/proto/http/responsewriter/responsewriter.proto b/avalanchego/proto/http/responsewriter/responsewriter.proto index f7ab2df2..55c6234b 100644 --- a/avalanchego/proto/http/responsewriter/responsewriter.proto +++ b/avalanchego/proto/http/responsewriter/responsewriter.proto @@ -46,19 +46,15 @@ message WriteHeaderRequest { } message HijackResponse { - // reserved for backward compatibility - // avalanchego <=v1.7.9 used the fields "1, 6, 7, 8" as an id to - // identify the gRPC server address which served the Conn, Reader - // and Writer services via the now removed service broker - reserved 1, 6 to 8; - string local_network = 2; + // local_network is the name of the network (for example, "tcp", "udp") + string local_network = 1; // local_string is string form of address - string local_string = 3; + string local_string = 2; // remote_network is the name of the network (for example, "tcp", "udp") - string remote_network = 4; + string remote_network = 3; // remote_string is string form of address - string remote_string = 5; + string remote_string = 4; // server_addr is the address of the gRPC server serving the Conn, Reader // and Writer services which facilitate Hijacking - string server_addr = 9; + string server_addr = 5; } diff --git a/avalanchego/proto/keystore/keystore.proto b/avalanchego/proto/keystore/keystore.proto index 0da76455..c59f1b4d 100644 --- a/avalanchego/proto/keystore/keystore.proto +++ b/avalanchego/proto/keystore/keystore.proto @@ -4,6 +4,10 @@ package keystore; option go_package = "github.com/ava-labs/avalanchego/proto/pb/keystore"; +service Keystore { + rpc GetDatabase(GetDatabaseRequest) returns (GetDatabaseResponse); +} + message GetDatabaseRequest { string username = 1; string password = 2; @@ -17,7 +21,3 @@ message GetDatabaseResponse { // server_addr is the address of the gRPC server hosting the Database service string server_addr = 2; } - -service Keystore { - rpc GetDatabase(GetDatabaseRequest) returns (GetDatabaseResponse); -} diff --git a/avalanchego/proto/messenger/messenger.proto b/avalanchego/proto/messenger/messenger.proto index 31e417db..027fcdeb 100644 --- a/avalanchego/proto/messenger/messenger.proto +++ b/avalanchego/proto/messenger/messenger.proto @@ -4,12 +4,12 @@ package messenger; option go_package = "github.com/ava-labs/avalanchego/proto/pb/messenger"; +service Messenger { + rpc Notify(NotifyRequest) returns (NotifyResponse); +} + message NotifyRequest { uint32 message = 1; } message NotifyResponse {} - -service Messenger { - rpc Notify(NotifyRequest) returns (NotifyResponse); -} diff --git a/avalanchego/proto/p2p/p2p.proto b/avalanchego/proto/p2p/p2p.proto new file mode 100644 index 00000000..375d5020 --- /dev/null +++ b/avalanchego/proto/p2p/p2p.proto @@ -0,0 +1,291 @@ +syntax = "proto3"; + +package p2p; + +// Reference: https://developers.google.com/protocol-buffers/docs/proto3 +option go_package = "github.com/ava-labs/avalanchego/proto/pb/p2p"; + +// Represents peer-to-peer messages. +// Only one type can be non-null. +message Message { + // NOTES + // Use "oneof" for each message type and set rest to null if not used. + // That is because when the compression is enabled, we don't want to include uncompressed fields. + oneof message { + // Gzip-compressed bytes of a "p2p.Message" whose "oneof" "message" field is + // NOT compressed_* BUT one of the message types (e.g. ping, pong, etc.). + // This field is only set if the message type supports compression. + bytes compressed_gzip = 1; + + // Fields lower than 10 are reserved for other compression algorithms. + // TODO: support COMPRESS_ZSTD + // TODO: support COMPRESS_SNAPPY + + // Network messages: + Ping ping = 11; + Pong pong = 12; + Version version = 13; + PeerList peer_list = 14; + + // State-sync messages: + GetStateSummaryFrontier get_state_summary_frontier = 15; + StateSummaryFrontier state_summary_frontier = 16; + GetAcceptedStateSummary get_accepted_state_summary = 17; + AcceptedStateSummary accepted_state_summary = 18; + + // Bootstrapping messages: + GetAcceptedFrontier get_accepted_frontier = 19; + AcceptedFrontier accepted_frontier = 20; + GetAccepted get_accepted = 21; + Accepted accepted = 22; + GetAncestors get_ancestors = 23; + Ancestors ancestors = 24; + + // Consensus messages: + Get get = 25; + Put put = 26; + PushQuery push_query = 27; + PullQuery pull_query = 28; + Chits chits = 29; + + // App messages: + AppRequest app_request = 30; + AppResponse app_response = 31; + AppGossip app_gossip = 32; + } +} + +// Message that the local node sends to its remote peers, +// in order to periodically check its uptime. +// +// On receiving "ping", the remote peer responds with the observed +// uptime value of the message sender in "pong" message. +message Ping {} + +// Contains the uptime of the message receiver (remote peer) +// from the sender's point of view, in response to "ping" message. +message Pong { + uint32 uptime_pct = 1; +} + +// The first outbound message that the local node sends to its remote peer +// when the connection is established. In order for the local node to be +// tracked as a valid peer by the remote peer, the fields must be valid. +// For instance, the network ID must be matched and timestamp should be in-sync. +// Otherwise, the remote peer closes the connection. +// ref. "avalanchego/network/peer#handleVersion" +// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/network#Network "Dispatch" +message Version { + uint32 network_id = 1; + uint64 my_time = 2; + bytes ip_addr = 3; + uint32 ip_port = 4; + string my_version = 5; + uint64 my_version_time = 6; + bytes sig = 7; + repeated bytes tracked_subnets = 8; +} + +// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/utils/ips#ClaimedIPPort +message ClaimedIpPort { + bytes x509_certificate = 1; + bytes ip_addr = 2; + uint32 ip_port = 3; + uint64 timestamp = 4; + bytes signature = 5; +} + +// Message that contains a list of peer information (IP, certs, etc.) +// in response to "version" message, and sent periodically to a set of +// validators. +// ref. "avalanchego/network/network#Dispatch.runtTimers" +// +// On receiving "peer_list", the engine starts/updates the tracking information +// of the remote peer. +message PeerList { + repeated ClaimedIpPort claimed_ip_ports = 1; +} + +message GetStateSummaryFrontier { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; +} + +message StateSummaryFrontier { + bytes chain_id = 1; + uint32 request_id = 2; + bytes summary = 3; +} + +message GetAcceptedStateSummary { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; + repeated uint64 heights = 4; +} + +message AcceptedStateSummary { + bytes chain_id = 1; + uint32 request_id = 2; + repeated bytes summary_ids = 3; +} + +// Message to request for the accepted frontier of the "remote" peer. +// For instance, the accepted frontier of X-chain DAG is the set of +// accepted vertices that do not have any accepted descendants (i.e., frontier). +// +// During bootstrap, the local node sends out "get_accepted_frontier" to validators +// (see "avalanchego/snow/engine/common/bootstrapper.Startup"). +// And the expected response is "accepted_frontier". +// +// See "snow/engine/common/bootstrapper.go#AcceptedFrontier". +message GetAcceptedFrontier { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; +} + +// Message that contains the list of accepted frontier in response to +// "get_accepted_frontier". For instance, on receiving "get_accepted_frontier", +// the X-chain engine responds with the accepted frontier of X-chain DAG. +// +// See "snow/engine/common/bootstrapper.go#AcceptedFrontier". +message AcceptedFrontier { + bytes chain_id = 1; + uint32 request_id = 2; + repeated bytes container_ids = 3; +} + +// Message to request for the accepted blocks/vertices of the "remote" peer. +// The local node sends out this message during bootstrap, following "get_accepted_frontier". +// Basically, sending the list of the accepted frontier and expects the response of +// the accepted IDs from the remote peer. +// +// See "avalanchego/snow/engine/common/bootstrapper.Startup" and "sendGetAccepted". +// See "snow/engine/common/bootstrapper.go#AcceptedFrontier". +message GetAccepted { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; + repeated bytes container_ids = 4; +} + +// Message that contains the list of accepted block/vertex IDs in response to +// "get_accepted". For instance, on receiving "get_accepted" that contains +// the sender's accepted frontier IDs, the X-chain engine responds only with +// the accepted vertex IDs of the X-chain DAG. +// +// See "snow/engine/avalanche#GetAccepted" and "SendAccepted". +// See "snow/engine/common/bootstrapper.go#Accepted". +message Accepted { + bytes chain_id = 1; + uint32 request_id = 2; + repeated bytes container_ids = 3; +} + +// Message that requests for the ancestors (parents) of the specified container ID. +// The engine bootstrapper sends this message to fetch all accepted containers +// in its transitive path. +// +// On receiving "get_ancestors", it responds with the ancestors' container bytes +// in "ancestors" message. +message GetAncestors { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; + bytes container_id = 4; +} + +// Message that contains the container bytes of the ancestors +// in response to "get_ancestors". +// +// On receiving "ancestors", the engine parses the containers and queues them +// to be accepted once we've received the entire chain history. +message Ancestors { + bytes chain_id = 1; + uint32 request_id = 2; + repeated bytes containers = 3; +} + +// Message that requests for the container data. +// +// On receiving "get", the engine looks up the container from the storage. +// If the container is found, it sends out the container data in "put" message. +message Get { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; + bytes container_id = 4; +} + +// Message that contains the container ID and its bytes in response to "get". +// +// On receiving "put", the engine parses the container and tries to issue it to consensus. +message Put { + bytes chain_id = 1; + uint32 request_id = 2; + bytes container = 3; + +// NOTE: "container_id" is deprecated in packer based serializer +} + +// Message that contains a preferred container ID and its container bytes +// in order to query other peers for their preferences of the container. +// For example, when a new container is issued, the engine sends out +// "push_query" and "pull_query" queries to ask other peers their preferences. +// See "avalanchego/snow/engine/common#SendMixedQuery". +// +// On receiving the "push_query", the engine parses the incoming container +// and tries to issue the container and all of its parents to the consensus, +// and calls "pull_query" handler to send "chits" for voting. +message PushQuery { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; + bytes container = 4; + +// NOTE: "container_id" is deprecated in packer based serializer +} + +// Message that contains a preferred container ID to query other peers +// for their preferences of the container. +// For example, when a new container is issued, the engine sends out +// "push_query" and "pull_query" queries to ask other peers their preferences. +// See "avalanchego/snow/engine/common#SendMixedQuery". +message PullQuery { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; + bytes container_id = 4; +} + +// Message that contains the votes/preferences of the local node, +// in response to "push_query" or "pull_query" (e.g., preferred frontier). +// +// On receiving "chits", the engine issues those preferred containers of vertices/blocks +// to the consensus. If the received container is not found, it responds back with +// "get" message to fetch the missing container from the remote peer. +message Chits { + bytes chain_id = 1; + uint32 request_id = 2; + repeated bytes container_ids = 3; +} + +message AppRequest { + bytes chain_id = 1; + uint32 request_id = 2; + uint64 deadline = 3; + bytes app_bytes = 4; +} + +message AppResponse { + bytes chain_id = 1; + uint32 request_id = 2; + bytes app_bytes = 3; +} + +message AppGossip { + bytes chain_id = 1; + bytes app_bytes = 2; +} diff --git a/avalanchego/proto/pb/http/http.pb.go b/avalanchego/proto/pb/http/http.pb.go index de08e88b..e845b2ce 100644 --- a/avalanchego/proto/pb/http/http.pb.go +++ b/avalanchego/proto/pb/http/http.pb.go @@ -333,20 +333,20 @@ type ConnectionState struct { NegotiatedProtocol string `protobuf:"bytes,5,opt,name=negotiated_protocol,json=negotiatedProtocol,proto3" json:"negotiated_protocol,omitempty"` // server_name is the value of the Server Name Indication extension sent by // the client - ServerName string `protobuf:"bytes,7,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` + ServerName string `protobuf:"bytes,6,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` // peer_certificates are the parsed certificates sent by the peer, in the // order in which they were sent - PeerCertificates *Certificates `protobuf:"bytes,8,opt,name=peer_certificates,json=peerCertificates,proto3" json:"peer_certificates,omitempty"` + PeerCertificates *Certificates `protobuf:"bytes,7,opt,name=peer_certificates,json=peerCertificates,proto3" json:"peer_certificates,omitempty"` // verified_chains is a list of one or more chains where the first element is // PeerCertificates[0] and the last element is from Config.RootCAs (on the // client side) or Config.ClientCAs (on the server side). - VerifiedChains []*Certificates `protobuf:"bytes,9,rep,name=verified_chains,json=verifiedChains,proto3" json:"verified_chains,omitempty"` + VerifiedChains []*Certificates `protobuf:"bytes,8,rep,name=verified_chains,json=verifiedChains,proto3" json:"verified_chains,omitempty"` // signed_certificate_timestamps is a list of SCTs provided by the peer // through the TLS handshake for the leaf certificate, if any - SignedCertificateTimestamps [][]byte `protobuf:"bytes,10,rep,name=signed_certificate_timestamps,json=signedCertificateTimestamps,proto3" json:"signed_certificate_timestamps,omitempty"` + SignedCertificateTimestamps [][]byte `protobuf:"bytes,9,rep,name=signed_certificate_timestamps,json=signedCertificateTimestamps,proto3" json:"signed_certificate_timestamps,omitempty"` // ocsp_response is a stapled Online Certificate Status Protocol (OCSP) // response provided by the peer for the leaf certificate, if any. - OcspResponse []byte `protobuf:"bytes,11,opt,name=ocsp_response,json=ocspResponse,proto3" json:"ocsp_response,omitempty"` + OcspResponse []byte `protobuf:"bytes,10,opt,name=ocsp_response,json=ocspResponse,proto3" json:"ocsp_response,omitempty"` } func (x *ConnectionState) Reset() { @@ -648,9 +648,9 @@ type ResponseWriter struct { // header returns the header map that will be sent by // WriteHeader. - Header []*Element `protobuf:"bytes,2,rep,name=header,proto3" json:"header,omitempty"` + Header []*Element `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty"` // server_addr is the address of the gRPC server hosting the Writer service - ServerAddr string `protobuf:"bytes,3,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` + ServerAddr string `protobuf:"bytes,2,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` } func (x *ResponseWriter) Reset() { @@ -932,7 +932,7 @@ var file_http_http_proto_rawDesc = []byte{ 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, - 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x22, 0xe1, 0x03, + 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x22, 0xd5, 0x03, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x68, @@ -946,98 +946,97 @@ var file_http_http_proto_rawDesc = []byte{ 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x74, 0x74, 0x70, + 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x10, 0x70, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, + 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x0e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x0a, 0x20, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x1b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x0c, 0x10, - 0x0d, 0x22, 0x96, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x55, 0x52, 0x4c, 0x52, 0x03, 0x75, - 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, - 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x2b, 0x0a, 0x11, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, - 0x67, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, - 0x72, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, - 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, - 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, - 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x6f, 0x72, 0x6d, - 0x12, 0x2a, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0c, 0x20, + 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x96, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x75, 0x72, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x55, 0x52, + 0x4c, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x25, + 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x66, 0x65, 0x72, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x04, + 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x2a, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x6f, 0x72, + 0x6d, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x46, 0x6f, 0x72, 0x6d, + 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, + 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x4b, + 0x65, 0x79, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x75, 0x72, 0x69, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x55, 0x72, 0x69, 0x12, 0x27, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x22, 0x58, + 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, + 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x22, 0x75, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x80, 0x01, 0x0a, 0x17, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x27, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x22, 0x6b, 0x0a, 0x18, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, + 0x64, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x12, 0x21, 0x0a, 0x0c, - 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0d, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, - 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, - 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, - 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x72, - 0x69, 0x12, 0x27, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x22, 0x5e, 0x0a, 0x0e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x06, - 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, - 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x41, 0x64, 0x64, 0x72, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x75, 0x0a, 0x0b, 0x48, 0x54, - 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x80, 0x01, 0x0a, 0x17, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, - 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x27, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, - 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x22, 0x6b, 0x0a, 0x18, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, - 0x6d, 0x70, 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, - 0x63, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, - 0x79, 0x32, 0x8a, 0x01, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x33, 0x0a, 0x06, 0x48, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x11, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x54, 0x54, 0x50, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, - 0x4d, 0x0a, 0x0c, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x12, - 0x1d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, - 0x70, 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, - 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2f, - 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, - 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, - 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x62, + 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x32, + 0x8a, 0x01, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x33, 0x0a, 0x06, 0x48, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x12, 0x11, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4d, 0x0a, + 0x0c, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1d, 0x2e, + 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, + 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x68, + 0x74, 0x74, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2f, 0x5a, 0x2d, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, + 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/avalanchego/proto/pb/http/responsewriter/responsewriter.pb.go b/avalanchego/proto/pb/http/responsewriter/responsewriter.pb.go index a38e62b0..e7df856e 100644 --- a/avalanchego/proto/pb/http/responsewriter/responsewriter.pb.go +++ b/avalanchego/proto/pb/http/responsewriter/responsewriter.pb.go @@ -245,16 +245,17 @@ type HijackResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - LocalNetwork string `protobuf:"bytes,2,opt,name=local_network,json=localNetwork,proto3" json:"local_network,omitempty"` + // local_network is the name of the network (for example, "tcp", "udp") + LocalNetwork string `protobuf:"bytes,1,opt,name=local_network,json=localNetwork,proto3" json:"local_network,omitempty"` // local_string is string form of address - LocalString string `protobuf:"bytes,3,opt,name=local_string,json=localString,proto3" json:"local_string,omitempty"` + LocalString string `protobuf:"bytes,2,opt,name=local_string,json=localString,proto3" json:"local_string,omitempty"` // remote_network is the name of the network (for example, "tcp", "udp") - RemoteNetwork string `protobuf:"bytes,4,opt,name=remote_network,json=remoteNetwork,proto3" json:"remote_network,omitempty"` + RemoteNetwork string `protobuf:"bytes,3,opt,name=remote_network,json=remoteNetwork,proto3" json:"remote_network,omitempty"` // remote_string is string form of address - RemoteString string `protobuf:"bytes,5,opt,name=remote_string,json=remoteString,proto3" json:"remote_string,omitempty"` + RemoteString string `protobuf:"bytes,4,opt,name=remote_string,json=remoteString,proto3" json:"remote_string,omitempty"` // server_addr is the address of the gRPC server serving the Conn, Reader // and Writer services which facilitate Hijacking - ServerAddr string `protobuf:"bytes,9,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` + ServerAddr string `protobuf:"bytes,5,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` } func (x *HijackResponse) Reset() { @@ -351,44 +352,43 @@ var file_http_responsewriter_responsewriter_proto_rawDesc = []byte{ 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x0e, 0x48, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x22, 0xc5, 0x01, 0x0a, 0x0e, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, + 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, + 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x23, 0x0a, 0x0d, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, - 0x64, 0x72, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x09, 0x32, 0xa8, - 0x02, 0x0a, 0x06, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x05, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0b, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x57, - 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x46, 0x6c, 0x75, - 0x73, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x45, 0x0a, 0x06, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x12, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x23, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x48, 0x69, 0x6a, 0x61, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3e, 0x5a, 0x3c, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, - 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, + 0x64, 0x72, 0x32, 0xa8, 0x02, 0x0a, 0x06, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x4e, 0x0a, + 0x05, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x74, 0x74, 0x70, + 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, + 0x0b, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x68, + 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, + 0x05, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x45, 0x0a, 0x06, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, + 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x23, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x48, + 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3e, 0x5a, + 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, + 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/avalanchego/proto/pb/p2p/p2p.pb.go b/avalanchego/proto/pb/p2p/p2p.pb.go new file mode 100644 index 00000000..03a8adf7 --- /dev/null +++ b/avalanchego/proto/pb/p2p/p2p.pb.go @@ -0,0 +1,2657 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: p2p/p2p.proto + +package p2p + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Represents peer-to-peer messages. +// Only one type can be non-null. +type Message struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // NOTES + // Use "oneof" for each message type and set rest to null if not used. + // That is because when the compression is enabled, we don't want to include uncompressed fields. + // + // Types that are assignable to Message: + // *Message_CompressedGzip + // *Message_Ping + // *Message_Pong + // *Message_Version + // *Message_PeerList + // *Message_GetStateSummaryFrontier + // *Message_StateSummaryFrontier_ + // *Message_GetAcceptedStateSummary + // *Message_AcceptedStateSummary_ + // *Message_GetAcceptedFrontier + // *Message_AcceptedFrontier_ + // *Message_GetAccepted + // *Message_Accepted_ + // *Message_GetAncestors + // *Message_Ancestors_ + // *Message_Get + // *Message_Put + // *Message_PushQuery + // *Message_PullQuery + // *Message_Chits + // *Message_AppRequest + // *Message_AppResponse + // *Message_AppGossip + Message isMessage_Message `protobuf_oneof:"message"` +} + +func (x *Message) Reset() { + *x = Message{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Message) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Message) ProtoMessage() {} + +func (x *Message) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Message.ProtoReflect.Descriptor instead. +func (*Message) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{0} +} + +func (m *Message) GetMessage() isMessage_Message { + if m != nil { + return m.Message + } + return nil +} + +func (x *Message) GetCompressedGzip() []byte { + if x, ok := x.GetMessage().(*Message_CompressedGzip); ok { + return x.CompressedGzip + } + return nil +} + +func (x *Message) GetPing() *Ping { + if x, ok := x.GetMessage().(*Message_Ping); ok { + return x.Ping + } + return nil +} + +func (x *Message) GetPong() *Pong { + if x, ok := x.GetMessage().(*Message_Pong); ok { + return x.Pong + } + return nil +} + +func (x *Message) GetVersion() *Version { + if x, ok := x.GetMessage().(*Message_Version); ok { + return x.Version + } + return nil +} + +func (x *Message) GetPeerList() *PeerList { + if x, ok := x.GetMessage().(*Message_PeerList); ok { + return x.PeerList + } + return nil +} + +func (x *Message) GetGetStateSummaryFrontier() *GetStateSummaryFrontier { + if x, ok := x.GetMessage().(*Message_GetStateSummaryFrontier); ok { + return x.GetStateSummaryFrontier + } + return nil +} + +func (x *Message) GetStateSummaryFrontier_() *StateSummaryFrontier { + if x, ok := x.GetMessage().(*Message_StateSummaryFrontier_); ok { + return x.StateSummaryFrontier_ + } + return nil +} + +func (x *Message) GetGetAcceptedStateSummary() *GetAcceptedStateSummary { + if x, ok := x.GetMessage().(*Message_GetAcceptedStateSummary); ok { + return x.GetAcceptedStateSummary + } + return nil +} + +func (x *Message) GetAcceptedStateSummary_() *AcceptedStateSummary { + if x, ok := x.GetMessage().(*Message_AcceptedStateSummary_); ok { + return x.AcceptedStateSummary_ + } + return nil +} + +func (x *Message) GetGetAcceptedFrontier() *GetAcceptedFrontier { + if x, ok := x.GetMessage().(*Message_GetAcceptedFrontier); ok { + return x.GetAcceptedFrontier + } + return nil +} + +func (x *Message) GetAcceptedFrontier_() *AcceptedFrontier { + if x, ok := x.GetMessage().(*Message_AcceptedFrontier_); ok { + return x.AcceptedFrontier_ + } + return nil +} + +func (x *Message) GetGetAccepted() *GetAccepted { + if x, ok := x.GetMessage().(*Message_GetAccepted); ok { + return x.GetAccepted + } + return nil +} + +func (x *Message) GetAccepted_() *Accepted { + if x, ok := x.GetMessage().(*Message_Accepted_); ok { + return x.Accepted_ + } + return nil +} + +func (x *Message) GetGetAncestors() *GetAncestors { + if x, ok := x.GetMessage().(*Message_GetAncestors); ok { + return x.GetAncestors + } + return nil +} + +func (x *Message) GetAncestors_() *Ancestors { + if x, ok := x.GetMessage().(*Message_Ancestors_); ok { + return x.Ancestors_ + } + return nil +} + +func (x *Message) GetGet() *Get { + if x, ok := x.GetMessage().(*Message_Get); ok { + return x.Get + } + return nil +} + +func (x *Message) GetPut() *Put { + if x, ok := x.GetMessage().(*Message_Put); ok { + return x.Put + } + return nil +} + +func (x *Message) GetPushQuery() *PushQuery { + if x, ok := x.GetMessage().(*Message_PushQuery); ok { + return x.PushQuery + } + return nil +} + +func (x *Message) GetPullQuery() *PullQuery { + if x, ok := x.GetMessage().(*Message_PullQuery); ok { + return x.PullQuery + } + return nil +} + +func (x *Message) GetChits() *Chits { + if x, ok := x.GetMessage().(*Message_Chits); ok { + return x.Chits + } + return nil +} + +func (x *Message) GetAppRequest() *AppRequest { + if x, ok := x.GetMessage().(*Message_AppRequest); ok { + return x.AppRequest + } + return nil +} + +func (x *Message) GetAppResponse() *AppResponse { + if x, ok := x.GetMessage().(*Message_AppResponse); ok { + return x.AppResponse + } + return nil +} + +func (x *Message) GetAppGossip() *AppGossip { + if x, ok := x.GetMessage().(*Message_AppGossip); ok { + return x.AppGossip + } + return nil +} + +type isMessage_Message interface { + isMessage_Message() +} + +type Message_CompressedGzip struct { + // Gzip-compressed bytes of a "p2p.Message" whose "oneof" "message" field is + // NOT compressed_* BUT one of the message types (e.g. ping, pong, etc.). + // This field is only set if the message type supports compression. + CompressedGzip []byte `protobuf:"bytes,1,opt,name=compressed_gzip,json=compressedGzip,proto3,oneof"` +} + +type Message_Ping struct { + // Network messages: + Ping *Ping `protobuf:"bytes,11,opt,name=ping,proto3,oneof"` +} + +type Message_Pong struct { + Pong *Pong `protobuf:"bytes,12,opt,name=pong,proto3,oneof"` +} + +type Message_Version struct { + Version *Version `protobuf:"bytes,13,opt,name=version,proto3,oneof"` +} + +type Message_PeerList struct { + PeerList *PeerList `protobuf:"bytes,14,opt,name=peer_list,json=peerList,proto3,oneof"` +} + +type Message_GetStateSummaryFrontier struct { + // State-sync messages: + GetStateSummaryFrontier *GetStateSummaryFrontier `protobuf:"bytes,15,opt,name=get_state_summary_frontier,json=getStateSummaryFrontier,proto3,oneof"` +} + +type Message_StateSummaryFrontier_ struct { + StateSummaryFrontier_ *StateSummaryFrontier `protobuf:"bytes,16,opt,name=state_summary_frontier,json=stateSummaryFrontier,proto3,oneof"` +} + +type Message_GetAcceptedStateSummary struct { + GetAcceptedStateSummary *GetAcceptedStateSummary `protobuf:"bytes,17,opt,name=get_accepted_state_summary,json=getAcceptedStateSummary,proto3,oneof"` +} + +type Message_AcceptedStateSummary_ struct { + AcceptedStateSummary_ *AcceptedStateSummary `protobuf:"bytes,18,opt,name=accepted_state_summary,json=acceptedStateSummary,proto3,oneof"` +} + +type Message_GetAcceptedFrontier struct { + // Bootstrapping messages: + GetAcceptedFrontier *GetAcceptedFrontier `protobuf:"bytes,19,opt,name=get_accepted_frontier,json=getAcceptedFrontier,proto3,oneof"` +} + +type Message_AcceptedFrontier_ struct { + AcceptedFrontier_ *AcceptedFrontier `protobuf:"bytes,20,opt,name=accepted_frontier,json=acceptedFrontier,proto3,oneof"` +} + +type Message_GetAccepted struct { + GetAccepted *GetAccepted `protobuf:"bytes,21,opt,name=get_accepted,json=getAccepted,proto3,oneof"` +} + +type Message_Accepted_ struct { + Accepted_ *Accepted `protobuf:"bytes,22,opt,name=accepted,proto3,oneof"` +} + +type Message_GetAncestors struct { + GetAncestors *GetAncestors `protobuf:"bytes,23,opt,name=get_ancestors,json=getAncestors,proto3,oneof"` +} + +type Message_Ancestors_ struct { + Ancestors_ *Ancestors `protobuf:"bytes,24,opt,name=ancestors,proto3,oneof"` +} + +type Message_Get struct { + // Consensus messages: + Get *Get `protobuf:"bytes,25,opt,name=get,proto3,oneof"` +} + +type Message_Put struct { + Put *Put `protobuf:"bytes,26,opt,name=put,proto3,oneof"` +} + +type Message_PushQuery struct { + PushQuery *PushQuery `protobuf:"bytes,27,opt,name=push_query,json=pushQuery,proto3,oneof"` +} + +type Message_PullQuery struct { + PullQuery *PullQuery `protobuf:"bytes,28,opt,name=pull_query,json=pullQuery,proto3,oneof"` +} + +type Message_Chits struct { + Chits *Chits `protobuf:"bytes,29,opt,name=chits,proto3,oneof"` +} + +type Message_AppRequest struct { + // App messages: + AppRequest *AppRequest `protobuf:"bytes,30,opt,name=app_request,json=appRequest,proto3,oneof"` +} + +type Message_AppResponse struct { + AppResponse *AppResponse `protobuf:"bytes,31,opt,name=app_response,json=appResponse,proto3,oneof"` +} + +type Message_AppGossip struct { + AppGossip *AppGossip `protobuf:"bytes,32,opt,name=app_gossip,json=appGossip,proto3,oneof"` +} + +func (*Message_CompressedGzip) isMessage_Message() {} + +func (*Message_Ping) isMessage_Message() {} + +func (*Message_Pong) isMessage_Message() {} + +func (*Message_Version) isMessage_Message() {} + +func (*Message_PeerList) isMessage_Message() {} + +func (*Message_GetStateSummaryFrontier) isMessage_Message() {} + +func (*Message_StateSummaryFrontier_) isMessage_Message() {} + +func (*Message_GetAcceptedStateSummary) isMessage_Message() {} + +func (*Message_AcceptedStateSummary_) isMessage_Message() {} + +func (*Message_GetAcceptedFrontier) isMessage_Message() {} + +func (*Message_AcceptedFrontier_) isMessage_Message() {} + +func (*Message_GetAccepted) isMessage_Message() {} + +func (*Message_Accepted_) isMessage_Message() {} + +func (*Message_GetAncestors) isMessage_Message() {} + +func (*Message_Ancestors_) isMessage_Message() {} + +func (*Message_Get) isMessage_Message() {} + +func (*Message_Put) isMessage_Message() {} + +func (*Message_PushQuery) isMessage_Message() {} + +func (*Message_PullQuery) isMessage_Message() {} + +func (*Message_Chits) isMessage_Message() {} + +func (*Message_AppRequest) isMessage_Message() {} + +func (*Message_AppResponse) isMessage_Message() {} + +func (*Message_AppGossip) isMessage_Message() {} + +// Message that the local node sends to its remote peers, +// in order to periodically check its uptime. +// +// On receiving "ping", the remote peer responds with the observed +// uptime value of the message sender in "pong" message. +type Ping struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Ping) Reset() { + *x = Ping{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ping.ProtoReflect.Descriptor instead. +func (*Ping) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{1} +} + +// Contains the uptime of the message receiver (remote peer) +// from the sender's point of view, in response to "ping" message. +type Pong struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UptimePct uint32 `protobuf:"varint,1,opt,name=uptime_pct,json=uptimePct,proto3" json:"uptime_pct,omitempty"` +} + +func (x *Pong) Reset() { + *x = Pong{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pong) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pong) ProtoMessage() {} + +func (x *Pong) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pong.ProtoReflect.Descriptor instead. +func (*Pong) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{2} +} + +func (x *Pong) GetUptimePct() uint32 { + if x != nil { + return x.UptimePct + } + return 0 +} + +// The first outbound message that the local node sends to its remote peer +// when the connection is established. In order for the local node to be +// tracked as a valid peer by the remote peer, the fields must be valid. +// For instance, the network ID must be matched and timestamp should be in-sync. +// Otherwise, the remote peer closes the connection. +// ref. "avalanchego/network/peer#handleVersion" +// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/network#Network "Dispatch" +type Version struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NetworkId uint32 `protobuf:"varint,1,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` + MyTime uint64 `protobuf:"varint,2,opt,name=my_time,json=myTime,proto3" json:"my_time,omitempty"` + IpAddr []byte `protobuf:"bytes,3,opt,name=ip_addr,json=ipAddr,proto3" json:"ip_addr,omitempty"` + IpPort uint32 `protobuf:"varint,4,opt,name=ip_port,json=ipPort,proto3" json:"ip_port,omitempty"` + MyVersion string `protobuf:"bytes,5,opt,name=my_version,json=myVersion,proto3" json:"my_version,omitempty"` + MyVersionTime uint64 `protobuf:"varint,6,opt,name=my_version_time,json=myVersionTime,proto3" json:"my_version_time,omitempty"` + Sig []byte `protobuf:"bytes,7,opt,name=sig,proto3" json:"sig,omitempty"` + TrackedSubnets [][]byte `protobuf:"bytes,8,rep,name=tracked_subnets,json=trackedSubnets,proto3" json:"tracked_subnets,omitempty"` +} + +func (x *Version) Reset() { + *x = Version{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Version) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Version) ProtoMessage() {} + +func (x *Version) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Version.ProtoReflect.Descriptor instead. +func (*Version) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{3} +} + +func (x *Version) GetNetworkId() uint32 { + if x != nil { + return x.NetworkId + } + return 0 +} + +func (x *Version) GetMyTime() uint64 { + if x != nil { + return x.MyTime + } + return 0 +} + +func (x *Version) GetIpAddr() []byte { + if x != nil { + return x.IpAddr + } + return nil +} + +func (x *Version) GetIpPort() uint32 { + if x != nil { + return x.IpPort + } + return 0 +} + +func (x *Version) GetMyVersion() string { + if x != nil { + return x.MyVersion + } + return "" +} + +func (x *Version) GetMyVersionTime() uint64 { + if x != nil { + return x.MyVersionTime + } + return 0 +} + +func (x *Version) GetSig() []byte { + if x != nil { + return x.Sig + } + return nil +} + +func (x *Version) GetTrackedSubnets() [][]byte { + if x != nil { + return x.TrackedSubnets + } + return nil +} + +// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/utils/ips#ClaimedIPPort +type ClaimedIpPort struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + X509Certificate []byte `protobuf:"bytes,1,opt,name=x509_certificate,json=x509Certificate,proto3" json:"x509_certificate,omitempty"` + IpAddr []byte `protobuf:"bytes,2,opt,name=ip_addr,json=ipAddr,proto3" json:"ip_addr,omitempty"` + IpPort uint32 `protobuf:"varint,3,opt,name=ip_port,json=ipPort,proto3" json:"ip_port,omitempty"` + Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *ClaimedIpPort) Reset() { + *x = ClaimedIpPort{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClaimedIpPort) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClaimedIpPort) ProtoMessage() {} + +func (x *ClaimedIpPort) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClaimedIpPort.ProtoReflect.Descriptor instead. +func (*ClaimedIpPort) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{4} +} + +func (x *ClaimedIpPort) GetX509Certificate() []byte { + if x != nil { + return x.X509Certificate + } + return nil +} + +func (x *ClaimedIpPort) GetIpAddr() []byte { + if x != nil { + return x.IpAddr + } + return nil +} + +func (x *ClaimedIpPort) GetIpPort() uint32 { + if x != nil { + return x.IpPort + } + return 0 +} + +func (x *ClaimedIpPort) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *ClaimedIpPort) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +// Message that contains a list of peer information (IP, certs, etc.) +// in response to "version" message, and sent periodically to a set of +// validators. +// ref. "avalanchego/network/network#Dispatch.runtTimers" +// +// On receiving "peer_list", the engine starts/updates the tracking information +// of the remote peer. +type PeerList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClaimedIpPorts []*ClaimedIpPort `protobuf:"bytes,1,rep,name=claimed_ip_ports,json=claimedIpPorts,proto3" json:"claimed_ip_ports,omitempty"` +} + +func (x *PeerList) Reset() { + *x = PeerList{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PeerList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeerList) ProtoMessage() {} + +func (x *PeerList) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PeerList.ProtoReflect.Descriptor instead. +func (*PeerList) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{5} +} + +func (x *PeerList) GetClaimedIpPorts() []*ClaimedIpPort { + if x != nil { + return x.ClaimedIpPorts + } + return nil +} + +type GetStateSummaryFrontier struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` +} + +func (x *GetStateSummaryFrontier) Reset() { + *x = GetStateSummaryFrontier{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetStateSummaryFrontier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStateSummaryFrontier) ProtoMessage() {} + +func (x *GetStateSummaryFrontier) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStateSummaryFrontier.ProtoReflect.Descriptor instead. +func (*GetStateSummaryFrontier) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{6} +} + +func (x *GetStateSummaryFrontier) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *GetStateSummaryFrontier) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetStateSummaryFrontier) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +type StateSummaryFrontier struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Summary []byte `protobuf:"bytes,3,opt,name=summary,proto3" json:"summary,omitempty"` +} + +func (x *StateSummaryFrontier) Reset() { + *x = StateSummaryFrontier{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StateSummaryFrontier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StateSummaryFrontier) ProtoMessage() {} + +func (x *StateSummaryFrontier) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StateSummaryFrontier.ProtoReflect.Descriptor instead. +func (*StateSummaryFrontier) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{7} +} + +func (x *StateSummaryFrontier) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *StateSummaryFrontier) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *StateSummaryFrontier) GetSummary() []byte { + if x != nil { + return x.Summary + } + return nil +} + +type GetAcceptedStateSummary struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` + Heights []uint64 `protobuf:"varint,4,rep,packed,name=heights,proto3" json:"heights,omitempty"` +} + +func (x *GetAcceptedStateSummary) Reset() { + *x = GetAcceptedStateSummary{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAcceptedStateSummary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAcceptedStateSummary) ProtoMessage() {} + +func (x *GetAcceptedStateSummary) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetAcceptedStateSummary.ProtoReflect.Descriptor instead. +func (*GetAcceptedStateSummary) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{8} +} + +func (x *GetAcceptedStateSummary) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *GetAcceptedStateSummary) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetAcceptedStateSummary) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +func (x *GetAcceptedStateSummary) GetHeights() []uint64 { + if x != nil { + return x.Heights + } + return nil +} + +type AcceptedStateSummary struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + SummaryIds [][]byte `protobuf:"bytes,3,rep,name=summary_ids,json=summaryIds,proto3" json:"summary_ids,omitempty"` +} + +func (x *AcceptedStateSummary) Reset() { + *x = AcceptedStateSummary{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AcceptedStateSummary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcceptedStateSummary) ProtoMessage() {} + +func (x *AcceptedStateSummary) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcceptedStateSummary.ProtoReflect.Descriptor instead. +func (*AcceptedStateSummary) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{9} +} + +func (x *AcceptedStateSummary) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *AcceptedStateSummary) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *AcceptedStateSummary) GetSummaryIds() [][]byte { + if x != nil { + return x.SummaryIds + } + return nil +} + +// Message to request for the accepted frontier of the "remote" peer. +// For instance, the accepted frontier of X-chain DAG is the set of +// accepted vertices that do not have any accepted descendants (i.e., frontier). +// +// During bootstrap, the local node sends out "get_accepted_frontier" to validators +// (see "avalanchego/snow/engine/common/bootstrapper.Startup"). +// And the expected response is "accepted_frontier". +// +// See "snow/engine/common/bootstrapper.go#AcceptedFrontier". +type GetAcceptedFrontier struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` +} + +func (x *GetAcceptedFrontier) Reset() { + *x = GetAcceptedFrontier{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAcceptedFrontier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAcceptedFrontier) ProtoMessage() {} + +func (x *GetAcceptedFrontier) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetAcceptedFrontier.ProtoReflect.Descriptor instead. +func (*GetAcceptedFrontier) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{10} +} + +func (x *GetAcceptedFrontier) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *GetAcceptedFrontier) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetAcceptedFrontier) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +// Message that contains the list of accepted frontier in response to +// "get_accepted_frontier". For instance, on receiving "get_accepted_frontier", +// the X-chain engine responds with the accepted frontier of X-chain DAG. +// +// See "snow/engine/common/bootstrapper.go#AcceptedFrontier". +type AcceptedFrontier struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + ContainerIds [][]byte `protobuf:"bytes,3,rep,name=container_ids,json=containerIds,proto3" json:"container_ids,omitempty"` +} + +func (x *AcceptedFrontier) Reset() { + *x = AcceptedFrontier{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AcceptedFrontier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcceptedFrontier) ProtoMessage() {} + +func (x *AcceptedFrontier) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcceptedFrontier.ProtoReflect.Descriptor instead. +func (*AcceptedFrontier) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{11} +} + +func (x *AcceptedFrontier) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *AcceptedFrontier) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *AcceptedFrontier) GetContainerIds() [][]byte { + if x != nil { + return x.ContainerIds + } + return nil +} + +// Message to request for the accepted blocks/vertices of the "remote" peer. +// The local node sends out this message during bootstrap, following "get_accepted_frontier". +// Basically, sending the list of the accepted frontier and expects the response of +// the accepted IDs from the remote peer. +// +// See "avalanchego/snow/engine/common/bootstrapper.Startup" and "sendGetAccepted". +// See "snow/engine/common/bootstrapper.go#AcceptedFrontier". +type GetAccepted struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` + ContainerIds [][]byte `protobuf:"bytes,4,rep,name=container_ids,json=containerIds,proto3" json:"container_ids,omitempty"` +} + +func (x *GetAccepted) Reset() { + *x = GetAccepted{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAccepted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAccepted) ProtoMessage() {} + +func (x *GetAccepted) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetAccepted.ProtoReflect.Descriptor instead. +func (*GetAccepted) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{12} +} + +func (x *GetAccepted) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *GetAccepted) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetAccepted) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +func (x *GetAccepted) GetContainerIds() [][]byte { + if x != nil { + return x.ContainerIds + } + return nil +} + +// Message that contains the list of accepted block/vertex IDs in response to +// "get_accepted". For instance, on receiving "get_accepted" that contains +// the sender's accepted frontier IDs, the X-chain engine responds only with +// the accepted vertex IDs of the X-chain DAG. +// +// See "snow/engine/avalanche#GetAccepted" and "SendAccepted". +// See "snow/engine/common/bootstrapper.go#Accepted". +type Accepted struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + ContainerIds [][]byte `protobuf:"bytes,3,rep,name=container_ids,json=containerIds,proto3" json:"container_ids,omitempty"` +} + +func (x *Accepted) Reset() { + *x = Accepted{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Accepted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Accepted) ProtoMessage() {} + +func (x *Accepted) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Accepted.ProtoReflect.Descriptor instead. +func (*Accepted) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{13} +} + +func (x *Accepted) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *Accepted) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *Accepted) GetContainerIds() [][]byte { + if x != nil { + return x.ContainerIds + } + return nil +} + +// Message that requests for the ancestors (parents) of the specified container ID. +// The engine bootstrapper sends this message to fetch all accepted containers +// in its transitive path. +// +// On receiving "get_ancestors", it responds with the ancestors' container bytes +// in "ancestors" message. +type GetAncestors struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` + ContainerId []byte `protobuf:"bytes,4,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` +} + +func (x *GetAncestors) Reset() { + *x = GetAncestors{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAncestors) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAncestors) ProtoMessage() {} + +func (x *GetAncestors) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetAncestors.ProtoReflect.Descriptor instead. +func (*GetAncestors) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{14} +} + +func (x *GetAncestors) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *GetAncestors) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetAncestors) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +func (x *GetAncestors) GetContainerId() []byte { + if x != nil { + return x.ContainerId + } + return nil +} + +// Message that contains the container bytes of the ancestors +// in response to "get_ancestors". +// +// On receiving "ancestors", the engine parses the containers and queues them +// to be accepted once we've received the entire chain history. +type Ancestors struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Containers [][]byte `protobuf:"bytes,3,rep,name=containers,proto3" json:"containers,omitempty"` +} + +func (x *Ancestors) Reset() { + *x = Ancestors{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ancestors) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ancestors) ProtoMessage() {} + +func (x *Ancestors) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ancestors.ProtoReflect.Descriptor instead. +func (*Ancestors) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{15} +} + +func (x *Ancestors) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *Ancestors) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *Ancestors) GetContainers() [][]byte { + if x != nil { + return x.Containers + } + return nil +} + +// Message that requests for the container data. +// +// On receiving "get", the engine looks up the container from the storage. +// If the container is found, it sends out the container data in "put" message. +type Get struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` + ContainerId []byte `protobuf:"bytes,4,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` +} + +func (x *Get) Reset() { + *x = Get{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Get) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Get) ProtoMessage() {} + +func (x *Get) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Get.ProtoReflect.Descriptor instead. +func (*Get) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{16} +} + +func (x *Get) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *Get) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *Get) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +func (x *Get) GetContainerId() []byte { + if x != nil { + return x.ContainerId + } + return nil +} + +// Message that contains the container ID and its bytes in response to "get". +// +// On receiving "put", the engine parses the container and tries to issue it to consensus. +type Put struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Container []byte `protobuf:"bytes,3,opt,name=container,proto3" json:"container,omitempty"` +} + +func (x *Put) Reset() { + *x = Put{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Put) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Put) ProtoMessage() {} + +func (x *Put) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Put.ProtoReflect.Descriptor instead. +func (*Put) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{17} +} + +func (x *Put) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *Put) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *Put) GetContainer() []byte { + if x != nil { + return x.Container + } + return nil +} + +// Message that contains a preferred container ID and its container bytes +// in order to query other peers for their preferences of the container. +// For example, when a new container is issued, the engine sends out +// "push_query" and "pull_query" queries to ask other peers their preferences. +// See "avalanchego/snow/engine/common#SendMixedQuery". +// +// On receiving the "push_query", the engine parses the incoming container +// and tries to issue the container and all of its parents to the consensus, +// and calls "pull_query" handler to send "chits" for voting. +type PushQuery struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` + Container []byte `protobuf:"bytes,4,opt,name=container,proto3" json:"container,omitempty"` +} + +func (x *PushQuery) Reset() { + *x = PushQuery{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushQuery) ProtoMessage() {} + +func (x *PushQuery) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushQuery.ProtoReflect.Descriptor instead. +func (*PushQuery) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{18} +} + +func (x *PushQuery) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *PushQuery) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *PushQuery) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +func (x *PushQuery) GetContainer() []byte { + if x != nil { + return x.Container + } + return nil +} + +// Message that contains a preferred container ID to query other peers +// for their preferences of the container. +// For example, when a new container is issued, the engine sends out +// "push_query" and "pull_query" queries to ask other peers their preferences. +// See "avalanchego/snow/engine/common#SendMixedQuery". +type PullQuery struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` + ContainerId []byte `protobuf:"bytes,4,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` +} + +func (x *PullQuery) Reset() { + *x = PullQuery{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PullQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PullQuery) ProtoMessage() {} + +func (x *PullQuery) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PullQuery.ProtoReflect.Descriptor instead. +func (*PullQuery) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{19} +} + +func (x *PullQuery) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *PullQuery) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *PullQuery) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +func (x *PullQuery) GetContainerId() []byte { + if x != nil { + return x.ContainerId + } + return nil +} + +// Message that contains the votes/preferences of the local node, +// in response to "push_query" or "pull_query" (e.g., preferred frontier). +// +// On receiving "chits", the engine issues those preferred containers of vertices/blocks +// to the consensus. If the received container is not found, it responds back with +// "get" message to fetch the missing container from the remote peer. +type Chits struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + ContainerIds [][]byte `protobuf:"bytes,3,rep,name=container_ids,json=containerIds,proto3" json:"container_ids,omitempty"` +} + +func (x *Chits) Reset() { + *x = Chits{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Chits) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Chits) ProtoMessage() {} + +func (x *Chits) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Chits.ProtoReflect.Descriptor instead. +func (*Chits) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{20} +} + +func (x *Chits) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *Chits) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *Chits) GetContainerIds() [][]byte { + if x != nil { + return x.ContainerIds + } + return nil +} + +type AppRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Deadline uint64 `protobuf:"varint,3,opt,name=deadline,proto3" json:"deadline,omitempty"` + AppBytes []byte `protobuf:"bytes,4,opt,name=app_bytes,json=appBytes,proto3" json:"app_bytes,omitempty"` +} + +func (x *AppRequest) Reset() { + *x = AppRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AppRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AppRequest) ProtoMessage() {} + +func (x *AppRequest) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AppRequest.ProtoReflect.Descriptor instead. +func (*AppRequest) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{21} +} + +func (x *AppRequest) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *AppRequest) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *AppRequest) GetDeadline() uint64 { + if x != nil { + return x.Deadline + } + return 0 +} + +func (x *AppRequest) GetAppBytes() []byte { + if x != nil { + return x.AppBytes + } + return nil +} + +type AppResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + RequestId uint32 `protobuf:"varint,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + AppBytes []byte `protobuf:"bytes,3,opt,name=app_bytes,json=appBytes,proto3" json:"app_bytes,omitempty"` +} + +func (x *AppResponse) Reset() { + *x = AppResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AppResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AppResponse) ProtoMessage() {} + +func (x *AppResponse) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AppResponse.ProtoReflect.Descriptor instead. +func (*AppResponse) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{22} +} + +func (x *AppResponse) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *AppResponse) GetRequestId() uint32 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *AppResponse) GetAppBytes() []byte { + if x != nil { + return x.AppBytes + } + return nil +} + +type AppGossip struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + AppBytes []byte `protobuf:"bytes,2,opt,name=app_bytes,json=appBytes,proto3" json:"app_bytes,omitempty"` +} + +func (x *AppGossip) Reset() { + *x = AppGossip{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_p2p_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AppGossip) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AppGossip) ProtoMessage() {} + +func (x *AppGossip) ProtoReflect() protoreflect.Message { + mi := &file_p2p_p2p_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AppGossip.ProtoReflect.Descriptor instead. +func (*AppGossip) Descriptor() ([]byte, []int) { + return file_p2p_p2p_proto_rawDescGZIP(), []int{23} +} + +func (x *AppGossip) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *AppGossip) GetAppBytes() []byte { + if x != nil { + return x.AppBytes + } + return nil +} + +var File_p2p_p2p_proto protoreflect.FileDescriptor + +var file_p2p_p2p_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x70, 0x32, 0x70, 0x2f, 0x70, 0x32, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x03, 0x70, 0x32, 0x70, 0x22, 0xfb, 0x09, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x29, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x67, + 0x7a, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x6f, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x47, 0x7a, 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x04, 0x70, + 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x70, 0x32, 0x70, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x04, + 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x70, 0x32, 0x70, + 0x2e, 0x50, 0x6f, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x28, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, + 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x09, 0x70, 0x65, 0x65, 0x72, 0x5f, + 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x32, 0x70, + 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x65, 0x65, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x1a, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, + 0x69, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x32, 0x70, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x46, + 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x17, 0x67, 0x65, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x69, + 0x65, 0x72, 0x12, 0x51, 0x0a, 0x16, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, + 0x14, 0x73, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x46, 0x72, 0x6f, + 0x6e, 0x74, 0x69, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x1a, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x32, 0x70, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x48, 0x00, 0x52, 0x17, 0x67, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x51, 0x0a, 0x16, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x48, 0x00, 0x52, + 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x4e, 0x0a, 0x15, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x13, 0x67, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x46, 0x72, 0x6f, + 0x6e, 0x74, 0x69, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x11, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x46, + 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0c, 0x67, + 0x65, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x15, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x67, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x16, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, + 0x38, 0x0a, 0x0d, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x67, 0x65, 0x74, + 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x09, 0x61, 0x6e, 0x63, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, + 0x32, 0x70, 0x2e, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x48, 0x00, 0x52, 0x09, + 0x61, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x03, 0x67, 0x65, 0x74, + 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x47, 0x65, 0x74, + 0x48, 0x00, 0x52, 0x03, 0x67, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x03, 0x70, 0x75, 0x74, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x50, 0x75, 0x74, 0x48, 0x00, + 0x52, 0x03, 0x70, 0x75, 0x74, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x32, 0x70, 0x2e, + 0x50, 0x75, 0x73, 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x48, 0x00, 0x52, 0x09, 0x70, 0x75, 0x73, + 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x6c, 0x6c, 0x5f, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x32, 0x70, + 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x48, 0x00, 0x52, 0x09, 0x70, 0x75, + 0x6c, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x63, 0x68, 0x69, 0x74, 0x73, + 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x43, 0x68, 0x69, + 0x74, 0x73, 0x48, 0x00, 0x52, 0x05, 0x63, 0x68, 0x69, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x0b, 0x61, + 0x70, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x35, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, + 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x32, 0x70, 0x2e, 0x41, 0x70, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x5f, 0x67, 0x6f, + 0x73, 0x73, 0x69, 0x70, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x32, 0x70, + 0x2e, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x48, 0x00, 0x52, 0x09, 0x61, 0x70, + 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x22, 0x25, 0x0a, 0x04, 0x50, 0x6f, + 0x6e, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x70, 0x63, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x63, + 0x74, 0x22, 0xf5, 0x01, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, + 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, + 0x6d, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6d, + 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x12, 0x17, + 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x69, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x79, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x79, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x79, 0x5f, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0d, 0x6d, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, + 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x6e, + 0x65, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x6b, + 0x65, 0x64, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x22, 0xa8, 0x01, 0x0a, 0x0d, 0x43, 0x6c, + 0x61, 0x69, 0x6d, 0x65, 0x64, 0x49, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x78, + 0x35, 0x30, 0x39, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x78, 0x35, 0x30, 0x39, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x12, + 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x69, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x22, 0x48, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x3c, 0x0a, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x5f, 0x70, + 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x32, 0x70, + 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x49, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0e, + 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x49, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x22, 0x6f, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22, + 0x6a, 0x0a, 0x14, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x46, + 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x89, 0x01, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x07, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x22, 0x71, 0x0a, 0x14, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, + 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, + 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x49, 0x64, 0x73, 0x22, 0x6b, 0x0a, 0x13, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, + 0x72, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x71, 0x0a, 0x10, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x0b, 0x47, + 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x69, 0x0a, 0x08, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x22, 0x87, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x22, 0x65, 0x0a, 0x09, 0x41, 0x6e, + 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x73, 0x22, 0x7e, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x21, + 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, + 0x64, 0x22, 0x5d, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x22, 0x7f, 0x0a, 0x09, 0x50, 0x75, 0x73, 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x19, 0x0a, + 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, + 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, + 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x22, 0x84, 0x01, 0x0a, 0x09, 0x50, 0x75, 0x6c, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, + 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, + 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x61, + 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x22, 0x66, 0x0a, 0x05, 0x43, 0x68, 0x69, 0x74, + 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x22, 0x7f, 0x0a, 0x0a, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, + 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x70, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x22, 0x64, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, + 0x70, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, + 0x70, 0x70, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x47, 0x6f, + 0x73, 0x73, 0x69, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x70, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x42, 0x79, 0x74, 0x65, 0x73, 0x42, 0x2e, 0x5a, 0x2c, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, + 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x70, 0x32, 0x70, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_p2p_p2p_proto_rawDescOnce sync.Once + file_p2p_p2p_proto_rawDescData = file_p2p_p2p_proto_rawDesc +) + +func file_p2p_p2p_proto_rawDescGZIP() []byte { + file_p2p_p2p_proto_rawDescOnce.Do(func() { + file_p2p_p2p_proto_rawDescData = protoimpl.X.CompressGZIP(file_p2p_p2p_proto_rawDescData) + }) + return file_p2p_p2p_proto_rawDescData +} + +var file_p2p_p2p_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_p2p_p2p_proto_goTypes = []interface{}{ + (*Message)(nil), // 0: p2p.Message + (*Ping)(nil), // 1: p2p.Ping + (*Pong)(nil), // 2: p2p.Pong + (*Version)(nil), // 3: p2p.Version + (*ClaimedIpPort)(nil), // 4: p2p.ClaimedIpPort + (*PeerList)(nil), // 5: p2p.PeerList + (*GetStateSummaryFrontier)(nil), // 6: p2p.GetStateSummaryFrontier + (*StateSummaryFrontier)(nil), // 7: p2p.StateSummaryFrontier + (*GetAcceptedStateSummary)(nil), // 8: p2p.GetAcceptedStateSummary + (*AcceptedStateSummary)(nil), // 9: p2p.AcceptedStateSummary + (*GetAcceptedFrontier)(nil), // 10: p2p.GetAcceptedFrontier + (*AcceptedFrontier)(nil), // 11: p2p.AcceptedFrontier + (*GetAccepted)(nil), // 12: p2p.GetAccepted + (*Accepted)(nil), // 13: p2p.Accepted + (*GetAncestors)(nil), // 14: p2p.GetAncestors + (*Ancestors)(nil), // 15: p2p.Ancestors + (*Get)(nil), // 16: p2p.Get + (*Put)(nil), // 17: p2p.Put + (*PushQuery)(nil), // 18: p2p.PushQuery + (*PullQuery)(nil), // 19: p2p.PullQuery + (*Chits)(nil), // 20: p2p.Chits + (*AppRequest)(nil), // 21: p2p.AppRequest + (*AppResponse)(nil), // 22: p2p.AppResponse + (*AppGossip)(nil), // 23: p2p.AppGossip +} +var file_p2p_p2p_proto_depIdxs = []int32{ + 1, // 0: p2p.Message.ping:type_name -> p2p.Ping + 2, // 1: p2p.Message.pong:type_name -> p2p.Pong + 3, // 2: p2p.Message.version:type_name -> p2p.Version + 5, // 3: p2p.Message.peer_list:type_name -> p2p.PeerList + 6, // 4: p2p.Message.get_state_summary_frontier:type_name -> p2p.GetStateSummaryFrontier + 7, // 5: p2p.Message.state_summary_frontier:type_name -> p2p.StateSummaryFrontier + 8, // 6: p2p.Message.get_accepted_state_summary:type_name -> p2p.GetAcceptedStateSummary + 9, // 7: p2p.Message.accepted_state_summary:type_name -> p2p.AcceptedStateSummary + 10, // 8: p2p.Message.get_accepted_frontier:type_name -> p2p.GetAcceptedFrontier + 11, // 9: p2p.Message.accepted_frontier:type_name -> p2p.AcceptedFrontier + 12, // 10: p2p.Message.get_accepted:type_name -> p2p.GetAccepted + 13, // 11: p2p.Message.accepted:type_name -> p2p.Accepted + 14, // 12: p2p.Message.get_ancestors:type_name -> p2p.GetAncestors + 15, // 13: p2p.Message.ancestors:type_name -> p2p.Ancestors + 16, // 14: p2p.Message.get:type_name -> p2p.Get + 17, // 15: p2p.Message.put:type_name -> p2p.Put + 18, // 16: p2p.Message.push_query:type_name -> p2p.PushQuery + 19, // 17: p2p.Message.pull_query:type_name -> p2p.PullQuery + 20, // 18: p2p.Message.chits:type_name -> p2p.Chits + 21, // 19: p2p.Message.app_request:type_name -> p2p.AppRequest + 22, // 20: p2p.Message.app_response:type_name -> p2p.AppResponse + 23, // 21: p2p.Message.app_gossip:type_name -> p2p.AppGossip + 4, // 22: p2p.PeerList.claimed_ip_ports:type_name -> p2p.ClaimedIpPort + 23, // [23:23] is the sub-list for method output_type + 23, // [23:23] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name +} + +func init() { file_p2p_p2p_proto_init() } +func file_p2p_p2p_proto_init() { + if File_p2p_p2p_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_p2p_p2p_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pong); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Version); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClaimedIpPort); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeerList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStateSummaryFrontier); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StateSummaryFrontier); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAcceptedStateSummary); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptedStateSummary); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAcceptedFrontier); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptedFrontier); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAccepted); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Accepted); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAncestors); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ancestors); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Get); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Put); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushQuery); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PullQuery); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Chits); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AppRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AppResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_p2p_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AppGossip); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_p2p_p2p_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Message_CompressedGzip)(nil), + (*Message_Ping)(nil), + (*Message_Pong)(nil), + (*Message_Version)(nil), + (*Message_PeerList)(nil), + (*Message_GetStateSummaryFrontier)(nil), + (*Message_StateSummaryFrontier_)(nil), + (*Message_GetAcceptedStateSummary)(nil), + (*Message_AcceptedStateSummary_)(nil), + (*Message_GetAcceptedFrontier)(nil), + (*Message_AcceptedFrontier_)(nil), + (*Message_GetAccepted)(nil), + (*Message_Accepted_)(nil), + (*Message_GetAncestors)(nil), + (*Message_Ancestors_)(nil), + (*Message_Get)(nil), + (*Message_Put)(nil), + (*Message_PushQuery)(nil), + (*Message_PullQuery)(nil), + (*Message_Chits)(nil), + (*Message_AppRequest)(nil), + (*Message_AppResponse)(nil), + (*Message_AppGossip)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_p2p_p2p_proto_rawDesc, + NumEnums: 0, + NumMessages: 24, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_p2p_p2p_proto_goTypes, + DependencyIndexes: file_p2p_p2p_proto_depIdxs, + MessageInfos: file_p2p_p2p_proto_msgTypes, + }.Build() + File_p2p_p2p_proto = out.File + file_p2p_p2p_proto_rawDesc = nil + file_p2p_p2p_proto_goTypes = nil + file_p2p_p2p_proto_depIdxs = nil +} diff --git a/avalanchego/proto/pb/vm/vm.pb.go b/avalanchego/proto/pb/vm/vm.pb.go index c12926fd..0d4f5eb6 100644 --- a/avalanchego/proto/pb/vm/vm.pb.go +++ b/avalanchego/proto/pb/vm/vm.pb.go @@ -41,7 +41,7 @@ type InitializeRequest struct { // server_addr is the address of the gRPC server which serves // the messenger, keystore, shared memory, blockchain alias, // subnet alias, and appSender services - ServerAddr string `protobuf:"bytes,20,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` + ServerAddr string `protobuf:"bytes,11,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` } func (x *InitializeRequest) Reset() { @@ -237,10 +237,10 @@ type VersionedDBServer struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // server_addr is the address of the gRPC server which serves the // Database service - ServerAddr string `protobuf:"bytes,3,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` + ServerAddr string `protobuf:"bytes,2,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` } func (x *VersionedDBServer) Reset() { @@ -518,7 +518,7 @@ type Handler struct { LockOptions uint32 `protobuf:"varint,2,opt,name=lock_options,json=lockOptions,proto3" json:"lock_options,omitempty"` // server_addr is the address of the gRPC server which serves the // HTTP service - ServerAddr string `protobuf:"bytes,4,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` + ServerAddr string `protobuf:"bytes,3,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` } func (x *Handler) Reset() { @@ -2537,7 +2537,7 @@ var file_vm_vm_proto_rawDesc = []byte{ 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x8f, 0x03, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, + 0x6f, 0x74, 0x6f, 0x22, 0x89, 0x03, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6e, @@ -2560,370 +2560,369 @@ var file_vm_vm_proto_rawDesc = []byte{ 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x6d, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x44, 0x42, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x09, 0x64, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1f, - 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x4a, - 0x04, 0x08, 0x0b, 0x10, 0x14, 0x22, 0xdd, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x54, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x64, 0x44, 0x42, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x41, 0x64, 0x64, 0x72, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x27, 0x0a, 0x0f, 0x53, - 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x22, 0x41, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x08, - 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, - 0x2e, 0x76, 0x6d, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x52, 0x08, 0x68, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x47, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x76, 0x6d, 0x2e, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x52, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x6b, - 0x0a, 0x07, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, - 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x41, 0x64, 0x64, 0x72, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xa9, 0x01, 0x0a, 0x12, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, - 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x38, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x29, 0x0a, 0x11, 0x50, 0x61, 0x72, 0x73, 0x65, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x22, 0xab, 0x01, 0x0a, 0x12, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x22, 0x21, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x02, 0x69, 0x64, 0x22, 0xc1, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, + 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x22, + 0xdd, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x64, + 0x12, 0x35, 0x0a, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0x4e, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x44, 0x42, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x22, + 0x27, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, + 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, + 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x41, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x27, 0x0a, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x76, 0x6d, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x52, + 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x47, 0x0a, 0x1c, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x08, 0x68, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x76, 0x6d, + 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x52, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x72, 0x73, 0x22, 0x65, 0x0a, 0x07, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6c, 0x6f, 0x63, + 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x22, 0xa9, 0x01, 0x0a, 0x12, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x26, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x50, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x2a, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x13, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x24, 0x0a, 0x12, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, - 0x69, 0x64, 0x22, 0x24, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2a, 0x0a, 0x0e, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x22, 0x2b, 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x99, 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x08, 0x64, - 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, - 0x69, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, - 0x13, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, - 0x64, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0e, - 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x17, - 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x39, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x29, 0x0a, 0x11, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x22, 0xab, 0x01, 0x0a, 0x12, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x21, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, + 0x64, 0x22, 0xc1, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x26, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x50, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2a, 0x0a, + 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x13, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x24, 0x0a, 0x12, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x24, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2a, 0x0a, 0x0e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x22, 0x2b, 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x99, 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x73, + 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x08, 0x64, 0x65, 0x61, + 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x13, 0x41, + 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6d, - 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x45, 0x0a, - 0x10, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2e, 0x0a, 0x13, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, - 0x64, 0x65, 0x49, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, - 0x62, 0x6c, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x6c, - 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4e, 0x75, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, - 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x37, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, - 0x72, 0x65, 0x74, 0x72, 0x69, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, - 0x74, 0x72, 0x69, 0x76, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6b, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c, 0x6b, 0x73, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x22, 0x34, 0x0a, 0x18, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, - 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4f, 0x0a, 0x19, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, - 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x19, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x45, 0x0a, 0x1a, - 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x6c, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0e, 0x41, 0x70, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, + 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x39, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x73, 0x67, + 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x45, 0x0a, 0x10, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x2e, 0x0a, 0x13, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, + 0x49, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x6c, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x6c, 0x6b, 0x49, - 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x65, 0x72, 0x72, 0x22, 0x5d, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, - 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, - 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, 0x69, - 0x6c, 0x79, 0x52, 0x0e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, - 0x65, 0x73, 0x22, 0x46, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x45, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x74, 0x0a, 0x22, 0x47, 0x65, - 0x74, 0x4f, 0x6e, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x10, - 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, - 0x22, 0x6d, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x10, 0x0a, - 0x03, 0x65, 0x72, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, - 0x30, 0x0a, 0x18, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x22, 0x55, 0x0a, 0x19, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, + 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, + 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x4e, 0x75, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x37, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x72, 0x65, + 0x74, 0x72, 0x69, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x74, 0x72, + 0x69, 0x76, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, + 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6b, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c, 0x6b, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, + 0x34, 0x0a, 0x18, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4f, 0x0a, 0x19, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, + 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x19, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x45, 0x0a, 0x1a, 0x47, 0x65, + 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x6c, 0x6b, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x6c, 0x6b, 0x49, 0x64, 0x12, + 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, + 0x72, 0x22, 0x5d, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x66, 0x61, + 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x69, + 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, + 0x52, 0x0e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, + 0x22, 0x46, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x45, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x74, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x4f, + 0x6e, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x30, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x65, 0x72, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x6d, + 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, - 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x31, 0x0a, - 0x19, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x22, 0x4a, 0x0a, 0x1a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x32, 0xb0, 0x10, 0x0a, - 0x02, 0x56, 0x4d, 0x12, 0x3b, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x12, 0x15, 0x2e, 0x76, 0x6d, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x49, 0x6e, - 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x35, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x76, - 0x6d, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, - 0x6f, 0x77, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x12, 0x44, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, - 0x76, 0x6d, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x14, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, - 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x20, 0x2e, 0x76, 0x6d, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x2e, 0x76, 0x6d, 0x2e, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x17, 0x2e, 0x76, 0x6d, 0x2e, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x72, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x30, 0x0a, + 0x18, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, + 0x55, 0x0a, 0x19, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x30, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x31, 0x0a, 0x19, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x4a, + 0x0a, 0x1a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x32, 0xb0, 0x10, 0x0a, 0x02, 0x56, + 0x4d, 0x12, 0x3b, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, + 0x15, 0x2e, 0x76, 0x6d, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, + 0x0a, 0x08, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x76, 0x6d, 0x2e, + 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x14, 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, + 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x44, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x76, 0x6d, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3c, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, - 0x76, 0x6d, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x12, 0x15, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x76, 0x6d, 0x2e, - 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x13, - 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x53, 0x65, 0x74, - 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x2e, 0x76, 0x6d, 0x2e, - 0x53, 0x65, 0x74, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x06, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, - 0x2e, 0x76, 0x6d, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x20, 0x2e, 0x76, 0x6d, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x2e, 0x76, 0x6d, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x12, 0x17, 0x2e, 0x76, 0x6d, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x76, 0x6d, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x70, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x17, 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4d, 0x73, 0x67, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, - 0x12, 0x10, 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, - 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x47, 0x61, - 0x74, 0x68, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3c, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x76, 0x6d, + 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x12, 0x15, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, + 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x35, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x13, 0x2e, 0x76, + 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x50, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x65, + 0x74, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x48, 0x65, + 0x61, 0x6c, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x76, - 0x6d, 0x2e, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x41, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, - 0x12, 0x17, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x6d, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, - 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x2e, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x53, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, - 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, - 0x79, 0x6e, 0x63, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6e, - 0x63, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x79, - 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x16, + 0x6d, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x36, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x76, 0x6d, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x70, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x43, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x17, 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x6e, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, - 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, - 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, - 0x0a, 0x11, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4a, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x12, 0x1a, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x35, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x12, 0x10, + 0x2e, 0x76, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x73, 0x67, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x47, 0x61, 0x74, 0x68, + 0x65, 0x72, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x76, 0x6d, 0x2e, + 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x17, + 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x50, 0x0a, 0x11, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, + 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x2e, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x53, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6e, + 0x63, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x1c, 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, + 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x6e, 0x63, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x67, + 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, + 0x47, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x76, 0x6d, + 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, + 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0b, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x16, 0x2e, 0x76, 0x6d, - 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x76, 0x6d, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, - 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x16, 0x2e, 0x76, 0x6d, - 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x2e, 0x76, 0x6d, 0x2e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x12, 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, - 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, - 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x76, 0x6d, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x1d, 0x2e, 0x76, 0x6d, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x12, 0x1a, 0x2e, 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x76, 0x6d, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x76, 0x6d, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x2e, 0x76, 0x6d, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x1d, + 0x2e, 0x76, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x76, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, + 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, + 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x76, 0x6d, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/avalanchego/proto/plugin/plugin.proto b/avalanchego/proto/plugin/plugin.proto index 38f5e7a1..8849a1d4 100644 --- a/avalanchego/proto/plugin/plugin.proto +++ b/avalanchego/proto/plugin/plugin.proto @@ -6,14 +6,12 @@ import "google/protobuf/empty.proto"; option go_package = "github.com/ava-labs/avalanchego/proto/pb/plugin"; -// To compile: protoc --go_out=plugins=grpc:. app.proto - -message ExitCodeResponse { - int32 exit_code = 1; -} - service Node { rpc Start(google.protobuf.Empty) returns (google.protobuf.Empty); rpc ExitCode(google.protobuf.Empty) returns (ExitCodeResponse); rpc Stop(google.protobuf.Empty) returns (google.protobuf.Empty); } + +message ExitCodeResponse { + int32 exit_code = 1; +} diff --git a/avalanchego/proto/rpcdb/rpcdb.proto b/avalanchego/proto/rpcdb/rpcdb.proto index c3201bee..c1800bd7 100644 --- a/avalanchego/proto/rpcdb/rpcdb.proto +++ b/avalanchego/proto/rpcdb/rpcdb.proto @@ -6,6 +6,21 @@ import "google/protobuf/empty.proto"; option go_package = "github.com/ava-labs/avalanchego/proto/pb/rpcdb"; +service Database { + rpc Has(HasRequest) returns (HasResponse); + rpc Get(GetRequest) returns (GetResponse); + rpc Put(PutRequest) returns (PutResponse); + rpc Delete(DeleteRequest) returns (DeleteResponse); + rpc Compact(CompactRequest) returns (CompactResponse); + rpc Close(CloseRequest) returns (CloseResponse); + rpc HealthCheck(google.protobuf.Empty) returns (HealthCheckResponse); + rpc WriteBatch(WriteBatchRequest) returns (WriteBatchResponse); + rpc NewIteratorWithStartAndPrefix(NewIteratorWithStartAndPrefixRequest) returns (NewIteratorWithStartAndPrefixResponse); + rpc IteratorNext(IteratorNextRequest) returns (IteratorNextResponse); + rpc IteratorError(IteratorErrorRequest) returns (IteratorErrorResponse); + rpc IteratorRelease(IteratorReleaseRequest) returns (IteratorReleaseResponse); +} + message HasRequest { bytes key = 1; } @@ -105,21 +120,3 @@ message IteratorReleaseResponse { message HealthCheckResponse { bytes details = 1; } - -service Database { - rpc Has(HasRequest) returns (HasResponse); - rpc Get(GetRequest) returns (GetResponse); - rpc Put(PutRequest) returns (PutResponse); - rpc Delete(DeleteRequest) returns (DeleteResponse); - rpc Compact(CompactRequest) returns (CompactResponse); - rpc Close(CloseRequest) returns (CloseResponse); - rpc HealthCheck(google.protobuf.Empty) returns (HealthCheckResponse); - - rpc WriteBatch(WriteBatchRequest) returns (WriteBatchResponse); - - rpc NewIteratorWithStartAndPrefix(NewIteratorWithStartAndPrefixRequest) returns (NewIteratorWithStartAndPrefixResponse); - - rpc IteratorNext(IteratorNextRequest) returns (IteratorNextResponse); - rpc IteratorError(IteratorErrorRequest) returns (IteratorErrorResponse); - rpc IteratorRelease(IteratorReleaseRequest) returns (IteratorReleaseResponse); -} diff --git a/avalanchego/proto/sharedmemory/sharedmemory.proto b/avalanchego/proto/sharedmemory/sharedmemory.proto index d4ab0320..62e8277c 100644 --- a/avalanchego/proto/sharedmemory/sharedmemory.proto +++ b/avalanchego/proto/sharedmemory/sharedmemory.proto @@ -4,6 +4,12 @@ package sharedmemory; option go_package = "github.com/ava-labs/avalanchego/proto/pb/sharedmemory"; +service SharedMemory { + rpc Get(GetRequest) returns (GetResponse); + rpc Indexed(IndexedRequest) returns (IndexedResponse); + rpc Apply(ApplyRequest) returns (ApplyResponse); +} + message BatchPut { bytes key = 1; bytes value = 2; @@ -68,9 +74,3 @@ message ApplyRequest { } message ApplyResponse {} - -service SharedMemory { - rpc Get(GetRequest) returns (GetResponse); - rpc Indexed(IndexedRequest) returns (IndexedResponse); - rpc Apply(ApplyRequest) returns (ApplyResponse); -} diff --git a/avalanchego/proto/subnetlookup/subnetlookup.proto b/avalanchego/proto/subnetlookup/subnetlookup.proto index 348b3376..c2e7f814 100644 --- a/avalanchego/proto/subnetlookup/subnetlookup.proto +++ b/avalanchego/proto/subnetlookup/subnetlookup.proto @@ -4,6 +4,10 @@ package subnetlookup; option go_package = "github.com/ava-labs/avalanchego/proto/pb/subnetlookup"; +service SubnetLookup { + rpc SubnetID(SubnetIDRequest) returns (SubnetIDResponse); +} + message SubnetIDRequest { bytes chain_id = 1; } @@ -11,7 +15,3 @@ message SubnetIDRequest { message SubnetIDResponse { bytes id = 1; } - -service SubnetLookup { - rpc SubnetID(SubnetIDRequest) returns (SubnetIDResponse); -} diff --git a/avalanchego/proto/vm/vm.proto b/avalanchego/proto/vm/vm.proto index 1d232f41..28a18afe 100644 --- a/avalanchego/proto/vm/vm.proto +++ b/avalanchego/proto/vm/vm.proto @@ -54,12 +54,6 @@ service VM { } message InitializeRequest { - // reserved for backward compatibility - // avalanchego <=v1.7.9 used the fields "11-19" as an id to - // identify the gRPC server address which served the messenger, - // keystore, shared memory, blockchain alias, subnet alias, and - // appSender services via the now removed service broker - reserved 11 to 19; uint32 network_id = 1; bytes subnet_id = 2; bytes chain_id = 3; @@ -73,7 +67,7 @@ message InitializeRequest { // server_addr is the address of the gRPC server which serves // the messenger, keystore, shared memory, blockchain alias, // subnet alias, and appSender services - string server_addr = 20; + string server_addr = 11; } message InitializeResponse { @@ -85,14 +79,10 @@ message InitializeResponse { } message VersionedDBServer { - // reserved for backward compatibility - // avalanchego <=v1.7.9 used the field "1" as an id to identify the gRPC server - // address which served the Database service via the now removed service broker - reserved 1; - string version = 2; + string version = 1; // server_addr is the address of the gRPC server which serves the // Database service - string server_addr = 3; + string server_addr = 2; } message SetStateRequest { @@ -116,15 +106,11 @@ message CreateStaticHandlersResponse { } message Handler { - // reserved for backward compatibility - // avalanchego <=v1.7.9 used the field "3" as an id to identify the gRPC server - // address which served the HTTP service via the now removed service broker - reserved 3; string prefix = 1; uint32 lock_options = 2; // server_addr is the address of the gRPC server which serves the // HTTP service - string server_addr = 4; + string server_addr = 3; } message BuildBlockResponse { diff --git a/avalanchego/pubsub/connection.go b/avalanchego/pubsub/connection.go index 143fc5e5..a3e71859 100644 --- a/avalanchego/pubsub/connection.go +++ b/avalanchego/pubsub/connection.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package pubsub diff --git a/avalanchego/pubsub/connections.go b/avalanchego/pubsub/connections.go index 831d14ba..52a0d433 100644 --- a/avalanchego/pubsub/connections.go +++ b/avalanchego/pubsub/connections.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package pubsub diff --git a/avalanchego/pubsub/filter_param.go b/avalanchego/pubsub/filter_param.go index b4d1096f..246015b5 100644 --- a/avalanchego/pubsub/filter_param.go +++ b/avalanchego/pubsub/filter_param.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package pubsub diff --git a/avalanchego/pubsub/filter_test.go b/avalanchego/pubsub/filter_test.go index e05e062b..8cd9161e 100644 --- a/avalanchego/pubsub/filter_test.go +++ b/avalanchego/pubsub/filter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package pubsub diff --git a/avalanchego/pubsub/filterer.go b/avalanchego/pubsub/filterer.go index adb59ca2..c3263728 100644 --- a/avalanchego/pubsub/filterer.go +++ b/avalanchego/pubsub/filterer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package pubsub diff --git a/avalanchego/pubsub/messages.go b/avalanchego/pubsub/messages.go index 184eec23..e9625b92 100644 --- a/avalanchego/pubsub/messages.go +++ b/avalanchego/pubsub/messages.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package pubsub diff --git a/avalanchego/pubsub/server.go b/avalanchego/pubsub/server.go index de2b69b8..b2d6738d 100644 --- a/avalanchego/pubsub/server.go +++ b/avalanchego/pubsub/server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package pubsub diff --git a/avalanchego/scripts/build_test.sh b/avalanchego/scripts/build_test.sh index 93c0c4aa..2515c4a5 100755 --- a/avalanchego/scripts/build_test.sh +++ b/avalanchego/scripts/build_test.sh @@ -4,4 +4,11 @@ set -o errexit set -o nounset set -o pipefail +# Directory above this script +AVALANCHE_PATH=$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd .. && pwd ) +# Load the versions +source "$AVALANCHE_PATH"/scripts/versions.sh +# Load the constants +source "$AVALANCHE_PATH"/scripts/constants.sh + go test -race -timeout="120s" -coverprofile="coverage.out" -covermode="atomic" $(go list ./... | grep -v /mocks | grep -v proto | grep -v tests) diff --git a/avalanchego/scripts/constants.sh b/avalanchego/scripts/constants.sh index db615bca..fdbe15c7 100644 --- a/avalanchego/scripts/constants.sh +++ b/avalanchego/scripts/constants.sh @@ -37,3 +37,9 @@ then which $CC > /dev/null || ( echo $CC must be available for static compilation && exit 1 ) static_ld_flags=' -extldflags "-static" -linkmode external ' fi + +# Set the CGO flags to use the portable version of BLST +# +# We use "export" here instead of just setting a bash variable because we need +# to pass this flag to all child processes spawned by the shell. +export CGO_CFLAGS="-O -D__BLST_PORTABLE__" diff --git a/avalanchego/scripts/lint.sh b/avalanchego/scripts/lint.sh index fc2b640b..6a2bfa6d 100755 --- a/avalanchego/scripts/lint.sh +++ b/avalanchego/scripts/lint.sh @@ -24,7 +24,7 @@ fi TESTS=${TESTS:-"golangci_lint license_header"} function test_golangci_lint { - go install -modcacherw -v github.com/golangci/golangci-lint/cmd/golangci-lint@v1.47.2 + go install -modcacherw -v github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49.0 golangci-lint run --config .golangci.yml } diff --git a/avalanchego/scripts/protobuf_codegen.sh b/avalanchego/scripts/protobuf_codegen.sh index 5e1c02d2..cd01448b 100755 --- a/avalanchego/scripts/protobuf_codegen.sh +++ b/avalanchego/scripts/protobuf_codegen.sh @@ -1,30 +1,39 @@ #!/bin/bash -# any version changes here should also be bumped in Dockerfile.buf -BUF_VERSION='1.5.0' -PROTOC_GEN_GO_VERSION='v1.28.0' -PROTOC_GEN_GO_GRPC_VERSION='1.2.0' - -# buf is required see:https://docs.buf.build/installation -# go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VERSION} -# go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION} - if ! [[ "$0" =~ scripts/protobuf_codegen.sh ]]; then echo "must be run from repository root" exit 255 fi +## install "buf" +# any version changes here should also be bumped in Dockerfile.buf +# ref. https://docs.buf.build/installation +# ref. https://github.com/bufbuild/buf/releases +BUF_VERSION='1.7.0' if [[ $(buf --version | cut -f2 -d' ') != "${BUF_VERSION}" ]]; then echo "could not find buf ${BUF_VERSION}, is it installed + in PATH?" exit 255 fi +## install "protoc-gen-go" +# any version changes here should also be bumped in Dockerfile.buf +# ref. https://github.com/protocolbuffers/protobuf-go/releases +PROTOC_GEN_GO_VERSION='v1.28.0' +go install -v google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VERSION} if [[ $(protoc-gen-go --version | cut -f2 -d' ') != "${PROTOC_GEN_GO_VERSION}" ]]; then + # e.g., protoc-gen-go v1.28.0 echo "could not find protoc-gen-go ${PROTOC_GEN_GO_VERSION}, is it installed + in PATH?" exit 255 fi +### install "protoc-gen-go-grpc" +# any version changes here should also be bumped in Dockerfile.buf +# ref. https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc +# ref. https://github.com/grpc/grpc-go/blob/master/cmd/protoc-gen-go-grpc/main.go +PROTOC_GEN_GO_GRPC_VERSION='1.2.0' +go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@v${PROTOC_GEN_GO_GRPC_VERSION} if [[ $(protoc-gen-go-grpc --version | cut -f2 -d' ') != "${PROTOC_GEN_GO_GRPC_VERSION}" ]]; then + # e.g., protoc-gen-go-grpc 1.2.0 echo "could not find protoc-gen-go-grpc ${PROTOC_GEN_GO_GRPC_VERSION}, is it installed + in PATH?" exit 255 fi @@ -38,11 +47,9 @@ fi cd $TARGET echo "Running protobuf fmt..." - buf format -w echo "Running protobuf lint check..." - buf lint if [[ $? -ne 0 ]]; then @@ -51,7 +58,6 @@ if [[ $? -ne 0 ]]; then fi echo "Re-generating protobuf..." - buf generate if [[ $? -ne 0 ]]; then diff --git a/avalanchego/scripts/tests.e2e.sh b/avalanchego/scripts/tests.e2e.sh index c22f9ce3..f90843a5 100755 --- a/avalanchego/scripts/tests.e2e.sh +++ b/avalanchego/scripts/tests.e2e.sh @@ -19,7 +19,20 @@ if [[ -z "${AVALANCHEGO_PATH}" ]]; then exit 255 fi +# Set the CGO flags to use the portable version of BLST +# +# We use "export" here instead of just setting a bash variable because we need +# to pass this flag to all child processes spawned by the shell. +export CGO_CFLAGS="-O -D__BLST_PORTABLE__" + ENABLE_WHITELIST_VTX_TESTS=${ENABLE_WHITELIST_VTX_TESTS:-false} +# ref. https://onsi.github.io/ginkgo/#spec-labels +GINKGO_LABEL_FILTER="!whitelist-tx" +if [[ ${ENABLE_WHITELIST_VTX_TESTS} == true ]]; then + # run only "whitelist-tx" tests, no other test + GINKGO_LABEL_FILTER="whitelist-tx" +fi +echo GINKGO_LABEL_FILTER: ${GINKGO_LABEL_FILTER} ################################# # download avalanche-network-runner @@ -27,7 +40,7 @@ ENABLE_WHITELIST_VTX_TESTS=${ENABLE_WHITELIST_VTX_TESTS:-false} # TODO: migrate to upstream avalanche-network-runner GOARCH=$(go env GOARCH) GOOS=$(go env GOOS) -NETWORK_RUNNER_VERSION=1.1.0 +NETWORK_RUNNER_VERSION=1.2.2 DOWNLOAD_PATH=/tmp/avalanche-network-runner.tar.gz DOWNLOAD_URL="https://github.com/ava-labs/avalanche-network-runner/releases/download/v${NETWORK_RUNNER_VERSION}/avalanche-network-runner_${NETWORK_RUNNER_VERSION}_${GOOS}_${GOARCH}.tar.gz" @@ -62,23 +75,6 @@ server \ PID=${!} ################################# -# By default, it runs all e2e test cases! -# Use "--ginkgo.skip" to skip tests. -# Use "--ginkgo.focus" to select tests. -# -# to run only ping tests: -# --ginkgo.focus "\[Local\] \[Ping\]" -# -# to run only X-Chain whitelist vtx tests: -# --ginkgo.focus "\[X-Chain\] \[WhitelistVtx\]" -# -# to run only X-Chain transfer tests: -# --ginkgo.focus "\[X-Chain\] \[Virtuous Transfer Tx AVAX\]" -# -# to skip all "Local" tests -# --ginkgo.skip "\[Local\]" -# -# set "--enable-whitelist-vtx-tests" to explicitly enable/disable whitelist vtx tests echo "running e2e tests against the local cluster with ${AVALANCHEGO_PATH}" ./tests/e2e/e2e.test \ --ginkgo.v \ @@ -86,8 +82,7 @@ echo "running e2e tests against the local cluster with ${AVALANCHEGO_PATH}" --network-runner-grpc-endpoint="0.0.0.0:12342" \ --network-runner-avalanchego-path=${AVALANCHEGO_PATH} \ --network-runner-avalanchego-log-level="WARN" \ ---test-keys-file=tests/test.insecure.secp256k1.keys \ ---enable-whitelist-vtx-tests=${ENABLE_WHITELIST_VTX_TESTS} \ +--test-keys-file=tests/test.insecure.secp256k1.keys --ginkgo.label-filter="${GINKGO_LABEL_FILTER}" \ && EXIT_CODE=$? || EXIT_CODE=$? kill ${PID} diff --git a/avalanchego/scripts/versions.sh b/avalanchego/scripts/versions.sh index 7288d80d..2f9242bb 100644 --- a/avalanchego/scripts/versions.sh +++ b/avalanchego/scripts/versions.sh @@ -7,5 +7,5 @@ # Set up the versions to be used # Don't export them as their used in the context of other calls -coreth_version=${CORETH_VERSION:-'v0.8.16-rc.2'} -avalanche_version=${AVALANCHE_VERSION:-'v1.7.18'} \ No newline at end of file +coreth_version=${CORETH_VERSION:-'v0.11.0-rc.4'} +avalanche_version=${AVALANCHE_VERSION:-'v1.9.0'} \ No newline at end of file diff --git a/avalanchego/snow/acceptor.go b/avalanchego/snow/acceptor.go index 33636cd8..54dbc20b 100644 --- a/avalanchego/snow/acceptor.go +++ b/avalanchego/snow/acceptor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snow diff --git a/avalanchego/snow/choices/decidable.go b/avalanchego/snow/choices/decidable.go index 123b05bf..946d196d 100644 --- a/avalanchego/snow/choices/decidable.go +++ b/avalanchego/snow/choices/decidable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package choices diff --git a/avalanchego/snow/choices/status.go b/avalanchego/snow/choices/status.go index 1faca915..5cc6d1c7 100644 --- a/avalanchego/snow/choices/status.go +++ b/avalanchego/snow/choices/status.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package choices diff --git a/avalanchego/snow/choices/status_test.go b/avalanchego/snow/choices/status_test.go index 97369901..78748843 100644 --- a/avalanchego/snow/choices/status_test.go +++ b/avalanchego/snow/choices/status_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package choices diff --git a/avalanchego/snow/choices/test_decidable.go b/avalanchego/snow/choices/test_decidable.go index cfd593fa..5c2b276b 100644 --- a/avalanchego/snow/choices/test_decidable.go +++ b/avalanchego/snow/choices/test_decidable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package choices diff --git a/avalanchego/snow/consensus/avalanche/consensus.go b/avalanchego/snow/consensus/avalanche/consensus.go index 4067dca5..1c1e5c6b 100644 --- a/avalanchego/snow/consensus/avalanche/consensus.go +++ b/avalanchego/snow/consensus/avalanche/consensus.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/consensus_test.go b/avalanchego/snow/consensus/avalanche/consensus_test.go index 3ad6fcd1..2a3902db 100644 --- a/avalanchego/snow/consensus/avalanche/consensus_test.go +++ b/avalanchego/snow/consensus/avalanche/consensus_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/factory.go b/avalanchego/snow/consensus/avalanche/factory.go index ce8072b4..05dd1e8f 100644 --- a/avalanchego/snow/consensus/avalanche/factory.go +++ b/avalanchego/snow/consensus/avalanche/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/parameters.go b/avalanchego/snow/consensus/avalanche/parameters.go index 46340b25..df0ce1bd 100644 --- a/avalanchego/snow/consensus/avalanche/parameters.go +++ b/avalanchego/snow/consensus/avalanche/parameters.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/parameters_test.go b/avalanchego/snow/consensus/avalanche/parameters_test.go index 0f66abd3..2bc3f677 100644 --- a/avalanchego/snow/consensus/avalanche/parameters_test.go +++ b/avalanchego/snow/consensus/avalanche/parameters_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal.go b/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal.go index 7fab72e1..70962f77 100644 --- a/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal.go +++ b/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll @@ -70,7 +70,7 @@ func (p *earlyTermNoTraversalPoll) Finished() bool { // votes will be applied to a single shared ancestor. In this case, the poll // can terminate early, iff there are not enough pending votes for this // ancestor to receive alpha votes. - partialVotes := ids.BitSet(0) + partialVotes := ids.BitSet64(0) for _, vote := range p.votes.List() { if voters := p.votes.GetSet(vote); voters.Len() < p.alpha { partialVotes.Union(voters) diff --git a/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal_test.go b/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal_test.go index 6a13cfb8..77603fb0 100644 --- a/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal_test.go +++ b/avalanchego/snow/consensus/avalanche/poll/early_term_no_traversal_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/avalanche/poll/interfaces.go b/avalanchego/snow/consensus/avalanche/poll/interfaces.go index 43cfd22b..b3682793 100644 --- a/avalanchego/snow/consensus/avalanche/poll/interfaces.go +++ b/avalanchego/snow/consensus/avalanche/poll/interfaces.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/avalanche/poll/no_early_term.go b/avalanchego/snow/consensus/avalanche/poll/no_early_term.go index 90cfa98f..1d859866 100644 --- a/avalanchego/snow/consensus/avalanche/poll/no_early_term.go +++ b/avalanchego/snow/consensus/avalanche/poll/no_early_term.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/avalanche/poll/no_early_term_test.go b/avalanchego/snow/consensus/avalanche/poll/no_early_term_test.go index ac2841b6..1b545232 100644 --- a/avalanchego/snow/consensus/avalanche/poll/no_early_term_test.go +++ b/avalanchego/snow/consensus/avalanche/poll/no_early_term_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/avalanche/poll/set.go b/avalanchego/snow/consensus/avalanche/poll/set.go index 655a7520..9d9d7087 100644 --- a/avalanchego/snow/consensus/avalanche/poll/set.go +++ b/avalanchego/snow/consensus/avalanche/poll/set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll @@ -47,7 +47,7 @@ type set struct { durPolls metric.Averager factory Factory // maps requestID -> poll - polls linkedhashmap.LinkedHashmap + polls linkedhashmap.LinkedHashmap[uint32, pollHolder] } // NewSet returns a new empty set of polls @@ -85,7 +85,7 @@ func NewSet( numPolls: numPolls, durPolls: durPolls, factory: factory, - polls: linkedhashmap.New(), + polls: linkedhashmap.New[uint32, pollHolder](), } } @@ -117,7 +117,7 @@ func (s *set) Add(requestID uint32, vdrs ids.NodeIDBag) bool { // Vote registers the connections response to a query for [id]. If there was no // query, or the response has already be registered, nothing is performed. func (s *set) Vote(requestID uint32, vdr ids.NodeID, votes []ids.ID) []ids.UniqueBag { - pollHolderIntf, exists := s.polls.Get(requestID) + holder, exists := s.polls.Get(requestID) if !exists { s.log.Verbo("dropping vote", zap.String("reason", "unknown poll"), @@ -127,7 +127,6 @@ func (s *set) Vote(requestID uint32, vdr ids.NodeID, votes []ids.ID) []ids.Uniqu return nil } - holder := pollHolderIntf.(pollHolder) p := holder.GetPoll() s.log.Verbo("processing votes", @@ -146,7 +145,7 @@ func (s *set) Vote(requestID uint32, vdr ids.NodeID, votes []ids.ID) []ids.Uniqu // iterate from oldest to newest iter := s.polls.NewIterator() for iter.Next() { - holder := iter.Value().(pollHolder) + holder := iter.Value() p := holder.GetPoll() if !p.Finished() { // since we're iterating from oldest to newest, if the next poll has not finished, diff --git a/avalanchego/snow/consensus/avalanche/poll/set_test.go b/avalanchego/snow/consensus/avalanche/poll/set_test.go index bb3948fc..e4a35ecc 100644 --- a/avalanchego/snow/consensus/avalanche/poll/set_test.go +++ b/avalanchego/snow/consensus/avalanche/poll/set_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/avalanche/test_vertex.go b/avalanchego/snow/consensus/avalanche/test_vertex.go index a40d78da..4390798a 100644 --- a/avalanchego/snow/consensus/avalanche/test_vertex.go +++ b/avalanchego/snow/consensus/avalanche/test_vertex.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/topological.go b/avalanchego/snow/consensus/avalanche/topological.go index 9a896f49..8962341b 100644 --- a/avalanchego/snow/consensus/avalanche/topological.go +++ b/avalanchego/snow/consensus/avalanche/topological.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche @@ -98,7 +98,7 @@ type Topological struct { type kahnNode struct { inDegree int - votes ids.BitSet + votes ids.BitSet64 } func (ta *Topological) Initialize( @@ -143,8 +143,6 @@ func (ta *Topological) Parameters() Parameters { return ta.params } func (ta *Topological) IsVirtuous(tx snowstorm.Tx) bool { return ta.cg.IsVirtuous(tx) } func (ta *Topological) Add(vtx Vertex) error { - ta.ctx.Log.AssertTrue(vtx != nil, "Attempting to insert nil vertex") - if vtx.Status().Decided() { return nil // Already decided this vertex } @@ -225,7 +223,7 @@ func (ta *Topological) RecordPoll(responses ids.UniqueBag) error { // If it isn't possible to have alpha votes for any transaction, then we can // just reset the confidence values in the conflict graph and not perform // any traversals. - partialVotes := ids.BitSet(0) + partialVotes := ids.BitSet64(0) for vote := range responses { votes := responses.GetSet(vote) partialVotes.Union(votes) diff --git a/avalanchego/snow/consensus/avalanche/topological_test.go b/avalanchego/snow/consensus/avalanche/topological_test.go index f8461d26..e1e27898 100644 --- a/avalanchego/snow/consensus/avalanche/topological_test.go +++ b/avalanchego/snow/consensus/avalanche/topological_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/transaction_vertex.go b/avalanchego/snow/consensus/avalanche/transaction_vertex.go index 3eab4ffe..0f35e972 100644 --- a/avalanchego/snow/consensus/avalanche/transaction_vertex.go +++ b/avalanchego/snow/consensus/avalanche/transaction_vertex.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/avalanche/vertex.go b/avalanchego/snow/consensus/avalanche/vertex.go index 3090ee8e..ebbcc47f 100644 --- a/avalanchego/snow/consensus/avalanche/vertex.go +++ b/avalanchego/snow/consensus/avalanche/vertex.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/consensus/metrics/height.go b/avalanchego/snow/consensus/metrics/height.go index caa7825f..93a98b8d 100644 --- a/avalanchego/snow/consensus/metrics/height.go +++ b/avalanchego/snow/consensus/metrics/height.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/snow/consensus/metrics/latency.go b/avalanchego/snow/consensus/metrics/latency.go index 47647440..bd377728 100644 --- a/avalanchego/snow/consensus/metrics/latency.go +++ b/avalanchego/snow/consensus/metrics/latency.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics @@ -49,7 +49,7 @@ type latency struct { // ProcessingEntries keeps track of the [opStart] that each item was issued // into the consensus instance. This is used to calculate the amount of time // to accept or reject the item. - processingEntries linkedhashmap.LinkedHashmap + processingEntries linkedhashmap.LinkedHashmap[ids.ID, opStart] // log reports anomalous events. log logging.Logger @@ -78,7 +78,7 @@ type latency struct { func NewLatency(metricName, descriptionName string, log logging.Logger, namespace string, reg prometheus.Registerer) (Latency, error) { errs := wrappers.Errs{} l := &latency{ - processingEntries: linkedhashmap.New(), + processingEntries: linkedhashmap.New[ids.ID, opStart](), log: log, numProcessing: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, @@ -127,7 +127,7 @@ func (l *latency) Issued(id ids.ID, pollNumber uint64) { } func (l *latency) Accepted(id ids.ID, pollNumber uint64) { - startIntf, ok := l.processingEntries.Get(id) + start, ok := l.processingEntries.Get(id) if !ok { l.log.Debug("unable to measure tx latency", zap.Stringer("status", choices.Accepted), @@ -137,8 +137,6 @@ func (l *latency) Accepted(id ids.ID, pollNumber uint64) { } l.processingEntries.Delete(id) - start := startIntf.(opStart) - l.pollsAccepted.Observe(float64(pollNumber - start.pollNumber)) duration := time.Since(start.time) @@ -147,7 +145,7 @@ func (l *latency) Accepted(id ids.ID, pollNumber uint64) { } func (l *latency) Rejected(id ids.ID, pollNumber uint64) { - startIntf, ok := l.processingEntries.Get(id) + start, ok := l.processingEntries.Get(id) if !ok { l.log.Debug("unable to measure tx latency", zap.Stringer("status", choices.Rejected), @@ -157,8 +155,6 @@ func (l *latency) Rejected(id ids.ID, pollNumber uint64) { } l.processingEntries.Delete(id) - start := startIntf.(opStart) - l.pollsRejected.Observe(float64(pollNumber - start.pollNumber)) duration := time.Since(start.time) @@ -167,12 +163,11 @@ func (l *latency) Rejected(id ids.ID, pollNumber uint64) { } func (l *latency) MeasureAndGetOldestDuration() time.Duration { - _, oldestTimeIntf, exists := l.processingEntries.Oldest() + _, oldestOp, exists := l.processingEntries.Oldest() if !exists { return 0 } - oldestTime := oldestTimeIntf.(opStart).time - return time.Since(oldestTime) + return time.Since(oldestOp.time) } func (l *latency) NumProcessing() int { diff --git a/avalanchego/snow/consensus/metrics/polls.go b/avalanchego/snow/consensus/metrics/polls.go index 27f5a99e..aff848e9 100644 --- a/avalanchego/snow/consensus/metrics/polls.go +++ b/avalanchego/snow/consensus/metrics/polls.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/snow/consensus/snowball/binary_slush.go b/avalanchego/snow/consensus/snowball/binary_slush.go index 90ae4133..8428c929 100644 --- a/avalanchego/snow/consensus/snowball/binary_slush.go +++ b/avalanchego/snow/consensus/snowball/binary_slush.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/binary_snowball.go b/avalanchego/snow/consensus/snowball/binary_snowball.go index bff2303f..349398b0 100644 --- a/avalanchego/snow/consensus/snowball/binary_snowball.go +++ b/avalanchego/snow/consensus/snowball/binary_snowball.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/binary_snowball_test.go b/avalanchego/snow/consensus/snowball/binary_snowball_test.go index c58fee11..1c796775 100644 --- a/avalanchego/snow/consensus/snowball/binary_snowball_test.go +++ b/avalanchego/snow/consensus/snowball/binary_snowball_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/binary_snowflake.go b/avalanchego/snow/consensus/snowball/binary_snowflake.go index 7b9b460c..f11b5584 100644 --- a/avalanchego/snow/consensus/snowball/binary_snowflake.go +++ b/avalanchego/snow/consensus/snowball/binary_snowflake.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/binary_snowflake_test.go b/avalanchego/snow/consensus/snowball/binary_snowflake_test.go index d55d3303..6d9f67b7 100644 --- a/avalanchego/snow/consensus/snowball/binary_snowflake_test.go +++ b/avalanchego/snow/consensus/snowball/binary_snowflake_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/consensus.go b/avalanchego/snow/consensus/snowball/consensus.go index c89944f8..87512b98 100644 --- a/avalanchego/snow/consensus/snowball/consensus.go +++ b/avalanchego/snow/consensus/snowball/consensus.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/consensus_performance_test.go b/avalanchego/snow/consensus/snowball/consensus_performance_test.go index 5edf0025..283b6677 100644 --- a/avalanchego/snow/consensus/snowball/consensus_performance_test.go +++ b/avalanchego/snow/consensus/snowball/consensus_performance_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/consensus_reversibility_test.go b/avalanchego/snow/consensus/snowball/consensus_reversibility_test.go index 8285e788..5d8be66d 100644 --- a/avalanchego/snow/consensus/snowball/consensus_reversibility_test.go +++ b/avalanchego/snow/consensus/snowball/consensus_reversibility_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/consensus_test.go b/avalanchego/snow/consensus/snowball/consensus_test.go index e80727b6..83ae2abd 100644 --- a/avalanchego/snow/consensus/snowball/consensus_test.go +++ b/avalanchego/snow/consensus/snowball/consensus_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/factory.go b/avalanchego/snow/consensus/snowball/factory.go index d5656d46..b5e4db87 100644 --- a/avalanchego/snow/consensus/snowball/factory.go +++ b/avalanchego/snow/consensus/snowball/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/flat.go b/avalanchego/snow/consensus/snowball/flat.go index 8a1e492b..42ce8685 100644 --- a/avalanchego/snow/consensus/snowball/flat.go +++ b/avalanchego/snow/consensus/snowball/flat.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/flat_test.go b/avalanchego/snow/consensus/snowball/flat_test.go index beeb13c8..2560daaa 100644 --- a/avalanchego/snow/consensus/snowball/flat_test.go +++ b/avalanchego/snow/consensus/snowball/flat_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/network_test.go b/avalanchego/snow/consensus/snowball/network_test.go index b46d4a34..2ce30e31 100644 --- a/avalanchego/snow/consensus/snowball/network_test.go +++ b/avalanchego/snow/consensus/snowball/network_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/nnary_slush.go b/avalanchego/snow/consensus/snowball/nnary_slush.go index ccbe6249..0b67afba 100644 --- a/avalanchego/snow/consensus/snowball/nnary_slush.go +++ b/avalanchego/snow/consensus/snowball/nnary_slush.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/nnary_snowball.go b/avalanchego/snow/consensus/snowball/nnary_snowball.go index 2ae43d99..fdbcc32a 100644 --- a/avalanchego/snow/consensus/snowball/nnary_snowball.go +++ b/avalanchego/snow/consensus/snowball/nnary_snowball.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/nnary_snowball_test.go b/avalanchego/snow/consensus/snowball/nnary_snowball_test.go index 025241e7..c48c6e0a 100644 --- a/avalanchego/snow/consensus/snowball/nnary_snowball_test.go +++ b/avalanchego/snow/consensus/snowball/nnary_snowball_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/nnary_snowflake.go b/avalanchego/snow/consensus/snowball/nnary_snowflake.go index cd0a368b..44e712e3 100644 --- a/avalanchego/snow/consensus/snowball/nnary_snowflake.go +++ b/avalanchego/snow/consensus/snowball/nnary_snowflake.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/nnary_snowflake_test.go b/avalanchego/snow/consensus/snowball/nnary_snowflake_test.go index 8ec22101..aab32831 100644 --- a/avalanchego/snow/consensus/snowball/nnary_snowflake_test.go +++ b/avalanchego/snow/consensus/snowball/nnary_snowflake_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/parameters.go b/avalanchego/snow/consensus/snowball/parameters.go index 0e1fb36d..893ceb3b 100644 --- a/avalanchego/snow/consensus/snowball/parameters.go +++ b/avalanchego/snow/consensus/snowball/parameters.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/parameters_test.go b/avalanchego/snow/consensus/snowball/parameters_test.go index 4e2d6242..3f37f4f1 100644 --- a/avalanchego/snow/consensus/snowball/parameters_test.go +++ b/avalanchego/snow/consensus/snowball/parameters_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/tree.go b/avalanchego/snow/consensus/snowball/tree.go index 4a9f5915..92c08e40 100644 --- a/avalanchego/snow/consensus/snowball/tree.go +++ b/avalanchego/snow/consensus/snowball/tree.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/tree_test.go b/avalanchego/snow/consensus/snowball/tree_test.go index 2a45e109..154b45ea 100644 --- a/avalanchego/snow/consensus/snowball/tree_test.go +++ b/avalanchego/snow/consensus/snowball/tree_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball @@ -154,9 +154,16 @@ func TestSnowballLastBinary(t *testing.T) { require.Equal(one, tree.Preference()) require.False(tree.Finalized()) + expected = "SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = false)) Bits = [0, 255)\n" + + " SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 1, SF(Confidence = 1, Finalized = false, SL(Preference = 1))) Bit = 255" + require.Equal(expected, tree.String()) + require.True(tree.RecordPoll(oneBag)) require.Equal(one, tree.Preference()) require.True(tree.Finalized()) + + expected = "SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 2, SF(Confidence = 2, Finalized = true, SL(Preference = 1))) Bit = 255" + require.Equal(expected, tree.String()) } func TestSnowballAddPreviouslyRejected(t *testing.T) { diff --git a/avalanchego/snow/consensus/snowball/unary_snowball.go b/avalanchego/snow/consensus/snowball/unary_snowball.go index a64442ea..e4cfdb68 100644 --- a/avalanchego/snow/consensus/snowball/unary_snowball.go +++ b/avalanchego/snow/consensus/snowball/unary_snowball.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/unary_snowball_test.go b/avalanchego/snow/consensus/snowball/unary_snowball_test.go index 42775d50..814f80ee 100644 --- a/avalanchego/snow/consensus/snowball/unary_snowball_test.go +++ b/avalanchego/snow/consensus/snowball/unary_snowball_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/unary_snowflake.go b/avalanchego/snow/consensus/snowball/unary_snowflake.go index c1ef5312..519a6d96 100644 --- a/avalanchego/snow/consensus/snowball/unary_snowflake.go +++ b/avalanchego/snow/consensus/snowball/unary_snowflake.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowball/unary_snowflake_test.go b/avalanchego/snow/consensus/snowball/unary_snowflake_test.go index d3139bf6..ae23df60 100644 --- a/avalanchego/snow/consensus/snowball/unary_snowflake_test.go +++ b/avalanchego/snow/consensus/snowball/unary_snowflake_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball diff --git a/avalanchego/snow/consensus/snowman/block.go b/avalanchego/snow/consensus/snowman/block.go index cb1dc97c..0321d64d 100644 --- a/avalanchego/snow/consensus/snowman/block.go +++ b/avalanchego/snow/consensus/snowman/block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/consensus.go b/avalanchego/snow/consensus/snowman/consensus.go index f21a2e18..3f091d12 100644 --- a/avalanchego/snow/consensus/snowman/consensus.go +++ b/avalanchego/snow/consensus/snowman/consensus.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/consensus_test.go b/avalanchego/snow/consensus/snowman/consensus_test.go index 6ae2f823..66fdf9bf 100644 --- a/avalanchego/snow/consensus/snowman/consensus_test.go +++ b/avalanchego/snow/consensus/snowman/consensus_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/factory.go b/avalanchego/snow/consensus/snowman/factory.go index 8b9fa64a..044fc33d 100644 --- a/avalanchego/snow/consensus/snowman/factory.go +++ b/avalanchego/snow/consensus/snowman/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/mock_block.go b/avalanchego/snow/consensus/snowman/mock_block.go new file mode 100644 index 00000000..1fabe798 --- /dev/null +++ b/avalanchego/snow/consensus/snowman/mock_block.go @@ -0,0 +1,163 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/snow/consensus/snowman (interfaces: Block) + +// Package snowman is a generated GoMock package. +package snowman + +import ( + reflect "reflect" + time "time" + + ids "github.com/ava-labs/avalanchego/ids" + choices "github.com/ava-labs/avalanchego/snow/choices" + gomock "github.com/golang/mock/gomock" +) + +// MockBlock is a mock of Block interface. +type MockBlock struct { + ctrl *gomock.Controller + recorder *MockBlockMockRecorder +} + +// MockBlockMockRecorder is the mock recorder for MockBlock. +type MockBlockMockRecorder struct { + mock *MockBlock +} + +// NewMockBlock creates a new mock instance. +func NewMockBlock(ctrl *gomock.Controller) *MockBlock { + mock := &MockBlock{ctrl: ctrl} + mock.recorder = &MockBlockMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBlock) EXPECT() *MockBlockMockRecorder { + return m.recorder +} + +// Accept mocks base method. +func (m *MockBlock) Accept() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Accept") + ret0, _ := ret[0].(error) + return ret0 +} + +// Accept indicates an expected call of Accept. +func (mr *MockBlockMockRecorder) Accept() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accept", reflect.TypeOf((*MockBlock)(nil).Accept)) +} + +// Bytes mocks base method. +func (m *MockBlock) Bytes() []byte { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Bytes") + ret0, _ := ret[0].([]byte) + return ret0 +} + +// Bytes indicates an expected call of Bytes. +func (mr *MockBlockMockRecorder) Bytes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bytes", reflect.TypeOf((*MockBlock)(nil).Bytes)) +} + +// Height mocks base method. +func (m *MockBlock) Height() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Height") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// Height indicates an expected call of Height. +func (mr *MockBlockMockRecorder) Height() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Height", reflect.TypeOf((*MockBlock)(nil).Height)) +} + +// ID mocks base method. +func (m *MockBlock) ID() ids.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ID") + ret0, _ := ret[0].(ids.ID) + return ret0 +} + +// ID indicates an expected call of ID. +func (mr *MockBlockMockRecorder) ID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockBlock)(nil).ID)) +} + +// Parent mocks base method. +func (m *MockBlock) Parent() ids.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Parent") + ret0, _ := ret[0].(ids.ID) + return ret0 +} + +// Parent indicates an expected call of Parent. +func (mr *MockBlockMockRecorder) Parent() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parent", reflect.TypeOf((*MockBlock)(nil).Parent)) +} + +// Reject mocks base method. +func (m *MockBlock) Reject() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Reject") + ret0, _ := ret[0].(error) + return ret0 +} + +// Reject indicates an expected call of Reject. +func (mr *MockBlockMockRecorder) Reject() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reject", reflect.TypeOf((*MockBlock)(nil).Reject)) +} + +// Status mocks base method. +func (m *MockBlock) Status() choices.Status { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Status") + ret0, _ := ret[0].(choices.Status) + return ret0 +} + +// Status indicates an expected call of Status. +func (mr *MockBlockMockRecorder) Status() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockBlock)(nil).Status)) +} + +// Timestamp mocks base method. +func (m *MockBlock) Timestamp() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Timestamp") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// Timestamp indicates an expected call of Timestamp. +func (mr *MockBlockMockRecorder) Timestamp() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Timestamp", reflect.TypeOf((*MockBlock)(nil).Timestamp)) +} + +// Verify mocks base method. +func (m *MockBlock) Verify() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Verify") + ret0, _ := ret[0].(error) + return ret0 +} + +// Verify indicates an expected call of Verify. +func (mr *MockBlockMockRecorder) Verify() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockBlock)(nil).Verify)) +} diff --git a/avalanchego/snow/consensus/snowman/network_test.go b/avalanchego/snow/consensus/snowman/network_test.go index fac23a6f..66e8f7e4 100644 --- a/avalanchego/snow/consensus/snowman/network_test.go +++ b/avalanchego/snow/consensus/snowman/network_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/oracle_block.go b/avalanchego/snow/consensus/snowman/oracle_block.go index fb446d95..80961a1c 100644 --- a/avalanchego/snow/consensus/snowman/oracle_block.go +++ b/avalanchego/snow/consensus/snowman/oracle_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal.go b/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal.go index 5ecced7f..062fe6fe 100644 --- a/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal.go +++ b/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal_test.go b/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal_test.go index 51a0d7b4..bc911430 100644 --- a/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal_test.go +++ b/avalanchego/snow/consensus/snowman/poll/early_term_no_traversal_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/snowman/poll/interfaces.go b/avalanchego/snow/consensus/snowman/poll/interfaces.go index a331c5ea..de30cae1 100644 --- a/avalanchego/snow/consensus/snowman/poll/interfaces.go +++ b/avalanchego/snow/consensus/snowman/poll/interfaces.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/snowman/poll/no_early_term.go b/avalanchego/snow/consensus/snowman/poll/no_early_term.go index 53bfd611..21293ef6 100644 --- a/avalanchego/snow/consensus/snowman/poll/no_early_term.go +++ b/avalanchego/snow/consensus/snowman/poll/no_early_term.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/snowman/poll/no_early_term_test.go b/avalanchego/snow/consensus/snowman/poll/no_early_term_test.go index 126661f4..b0258ecc 100644 --- a/avalanchego/snow/consensus/snowman/poll/no_early_term_test.go +++ b/avalanchego/snow/consensus/snowman/poll/no_early_term_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/snowman/poll/set.go b/avalanchego/snow/consensus/snowman/poll/set.go index 852c264f..62f464e9 100644 --- a/avalanchego/snow/consensus/snowman/poll/set.go +++ b/avalanchego/snow/consensus/snowman/poll/set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll @@ -42,7 +42,7 @@ type set struct { durPolls metric.Averager factory Factory // maps requestID -> poll - polls linkedhashmap.LinkedHashmap + polls linkedhashmap.LinkedHashmap[uint32, pollHolder] } // NewSet returns a new empty set of polls @@ -80,7 +80,7 @@ func NewSet( numPolls: numPolls, durPolls: durPolls, factory: factory, - polls: linkedhashmap.New(), + polls: linkedhashmap.New[uint32, pollHolder](), } } @@ -112,7 +112,7 @@ func (s *set) Add(requestID uint32, vdrs ids.NodeIDBag) bool { // Vote registers the connections response to a query for [id]. If there was no // query, or the response has already be registered, nothing is performed. func (s *set) Vote(requestID uint32, vdr ids.NodeID, vote ids.ID) []ids.Bag { - pollHolderIntf, exists := s.polls.Get(requestID) + holder, exists := s.polls.Get(requestID) if !exists { s.log.Verbo("dropping vote", zap.String("reason", "unknown poll"), @@ -122,7 +122,6 @@ func (s *set) Vote(requestID uint32, vdr ids.NodeID, vote ids.ID) []ids.Bag { return nil } - holder := pollHolderIntf.(pollHolder) p := holder.GetPoll() s.log.Verbo("processing vote", @@ -146,7 +145,7 @@ func (s *set) processFinishedPolls() []ids.Bag { // iterate from oldest to newest iter := s.polls.NewIterator() for iter.Next() { - holder := iter.Value().(pollHolder) + holder := iter.Value() p := holder.GetPoll() if !p.Finished() { // since we're iterating from oldest to newest, if the next poll has not finished, @@ -173,7 +172,7 @@ func (s *set) processFinishedPolls() []ids.Bag { // Drop registers the connections response to a query for [id]. If there was no // query, or the response has already be registered, nothing is performed. func (s *set) Drop(requestID uint32, vdr ids.NodeID) []ids.Bag { - pollHolderIntf, exists := s.polls.Get(requestID) + holder, exists := s.polls.Get(requestID) if !exists { s.log.Verbo("dropping vote", zap.String("reason", "unknown poll"), @@ -188,8 +187,7 @@ func (s *set) Drop(requestID uint32, vdr ids.NodeID) []ids.Bag { zap.Uint32("requestID", requestID), ) - pollHolder := pollHolderIntf.(pollHolder) - poll := pollHolder.GetPoll() + poll := holder.GetPoll() poll.Drop(vdr) if !poll.Finished() { diff --git a/avalanchego/snow/consensus/snowman/poll/set_test.go b/avalanchego/snow/consensus/snowman/poll/set_test.go index 4a1477df..0dfbc325 100644 --- a/avalanchego/snow/consensus/snowman/poll/set_test.go +++ b/avalanchego/snow/consensus/snowman/poll/set_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package poll diff --git a/avalanchego/snow/consensus/snowman/snowman_block.go b/avalanchego/snow/consensus/snowman/snowman_block.go index a6e75a01..d04d0349 100644 --- a/avalanchego/snow/consensus/snowman/snowman_block.go +++ b/avalanchego/snow/consensus/snowman/snowman_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/test_block.go b/avalanchego/snow/consensus/snowman/test_block.go index f3e7ccfc..241688b5 100644 --- a/avalanchego/snow/consensus/snowman/test_block.go +++ b/avalanchego/snow/consensus/snowman/test_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/topological.go b/avalanchego/snow/consensus/snowman/topological.go index f573523a..68c9c760 100644 --- a/avalanchego/snow/consensus/snowman/topological.go +++ b/avalanchego/snow/consensus/snowman/topological.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowman/topological_test.go b/avalanchego/snow/consensus/snowman/topological_test.go index 95f82cb4..ba8c16d1 100644 --- a/avalanchego/snow/consensus/snowman/topological_test.go +++ b/avalanchego/snow/consensus/snowman/topological_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/consensus/snowstorm/acceptor.go b/avalanchego/snow/consensus/snowstorm/acceptor.go index 0fd71f35..6221588b 100644 --- a/avalanchego/snow/consensus/snowstorm/acceptor.go +++ b/avalanchego/snow/consensus/snowstorm/acceptor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/benchmark_test.go b/avalanchego/snow/consensus/snowstorm/benchmark_test.go index f8948645..45d11675 100644 --- a/avalanchego/snow/consensus/snowstorm/benchmark_test.go +++ b/avalanchego/snow/consensus/snowstorm/benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/consensus.go b/avalanchego/snow/consensus/snowstorm/consensus.go index ea9a86b8..458fea95 100644 --- a/avalanchego/snow/consensus/snowstorm/consensus.go +++ b/avalanchego/snow/consensus/snowstorm/consensus.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/consensus_test.go b/avalanchego/snow/consensus/snowstorm/consensus_test.go index be8d8d72..d65c8847 100644 --- a/avalanchego/snow/consensus/snowstorm/consensus_test.go +++ b/avalanchego/snow/consensus/snowstorm/consensus_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/directed.go b/avalanchego/snow/consensus/snowstorm/directed.go index fc95763f..658f22d4 100644 --- a/avalanchego/snow/consensus/snowstorm/directed.go +++ b/avalanchego/snow/consensus/snowstorm/directed.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/directed_test.go b/avalanchego/snow/consensus/snowstorm/directed_test.go index ad22e06f..53acc121 100644 --- a/avalanchego/snow/consensus/snowstorm/directed_test.go +++ b/avalanchego/snow/consensus/snowstorm/directed_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/factory.go b/avalanchego/snow/consensus/snowstorm/factory.go index 71f8c122..6d70e181 100644 --- a/avalanchego/snow/consensus/snowstorm/factory.go +++ b/avalanchego/snow/consensus/snowstorm/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/network_test.go b/avalanchego/snow/consensus/snowstorm/network_test.go index 3ae1d440..248721ae 100644 --- a/avalanchego/snow/consensus/snowstorm/network_test.go +++ b/avalanchego/snow/consensus/snowstorm/network_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/rejector.go b/avalanchego/snow/consensus/snowstorm/rejector.go index b2a76d06..99dc0379 100644 --- a/avalanchego/snow/consensus/snowstorm/rejector.go +++ b/avalanchego/snow/consensus/snowstorm/rejector.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/snowball.go b/avalanchego/snow/consensus/snowstorm/snowball.go index 424ab993..76c720be 100644 --- a/avalanchego/snow/consensus/snowstorm/snowball.go +++ b/avalanchego/snow/consensus/snowstorm/snowball.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/stringer.go b/avalanchego/snow/consensus/snowstorm/stringer.go index fae6e049..273d1880 100644 --- a/avalanchego/snow/consensus/snowstorm/stringer.go +++ b/avalanchego/snow/consensus/snowstorm/stringer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/test_tx.go b/avalanchego/snow/consensus/snowstorm/test_tx.go index d2e06114..a62d99e5 100644 --- a/avalanchego/snow/consensus/snowstorm/test_tx.go +++ b/avalanchego/snow/consensus/snowstorm/test_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/consensus/snowstorm/tx.go b/avalanchego/snow/consensus/snowstorm/tx.go index f74b69c7..150b7911 100644 --- a/avalanchego/snow/consensus/snowstorm/tx.go +++ b/avalanchego/snow/consensus/snowstorm/tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowstorm diff --git a/avalanchego/snow/context.go b/avalanchego/snow/context.go index c03da3e6..311f5af6 100644 --- a/avalanchego/snow/context.go +++ b/avalanchego/snow/context.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snow diff --git a/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper.go b/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper.go index 2f0e343f..65c2bb8a 100644 --- a/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper.go +++ b/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper_test.go b/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper_test.go index 85116184..a95b68ee 100644 --- a/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper_test.go +++ b/avalanchego/snow/engine/avalanche/bootstrap/bootstrapper_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/avalanche/bootstrap/config.go b/avalanchego/snow/engine/avalanche/bootstrap/config.go index 48e5338d..ef65a1d2 100644 --- a/avalanchego/snow/engine/avalanche/bootstrap/config.go +++ b/avalanchego/snow/engine/avalanche/bootstrap/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/avalanche/bootstrap/metrics.go b/avalanchego/snow/engine/avalanche/bootstrap/metrics.go index 70c999fd..8c992b28 100644 --- a/avalanchego/snow/engine/avalanche/bootstrap/metrics.go +++ b/avalanchego/snow/engine/avalanche/bootstrap/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/avalanche/bootstrap/tx_job.go b/avalanchego/snow/engine/avalanche/bootstrap/tx_job.go index cdbcf6c4..d5ba7867 100644 --- a/avalanchego/snow/engine/avalanche/bootstrap/tx_job.go +++ b/avalanchego/snow/engine/avalanche/bootstrap/tx_job.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/avalanche/bootstrap/vertex_job.go b/avalanchego/snow/engine/avalanche/bootstrap/vertex_job.go index 243df7b1..5790478a 100644 --- a/avalanchego/snow/engine/avalanche/bootstrap/vertex_job.go +++ b/avalanchego/snow/engine/avalanche/bootstrap/vertex_job.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/avalanche/config.go b/avalanchego/snow/engine/avalanche/config.go index 8ca97880..8c716b63 100644 --- a/avalanchego/snow/engine/avalanche/config.go +++ b/avalanchego/snow/engine/avalanche/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/avalanche/config_test.go b/avalanchego/snow/engine/avalanche/config_test.go index 78ffa7fa..c20ea448 100644 --- a/avalanchego/snow/engine/avalanche/config_test.go +++ b/avalanchego/snow/engine/avalanche/config_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/avalanche/engine.go b/avalanchego/snow/engine/avalanche/engine.go index c3daeb0d..72c3a6bd 100644 --- a/avalanchego/snow/engine/avalanche/engine.go +++ b/avalanchego/snow/engine/avalanche/engine.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/avalanche/getter/getter.go b/avalanchego/snow/engine/avalanche/getter/getter.go index f2f593cb..8b04fb3c 100644 --- a/avalanchego/snow/engine/avalanche/getter/getter.go +++ b/avalanchego/snow/engine/avalanche/getter/getter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package getter @@ -142,7 +142,7 @@ func (gh *getter) GetAncestors(nodeID ids.NodeID, requestID uint32, vtxID ids.ID func (gh *getter) Get(nodeID ids.NodeID, requestID uint32, vtxID ids.ID) error { // If this engine has access to the requested vertex, provide it if vtx, err := gh.storage.GetVtx(vtxID); err == nil { - gh.sender.SendPut(nodeID, requestID, vtxID, vtx.Bytes()) + gh.sender.SendPut(nodeID, requestID, vtx.Bytes()) } return nil } diff --git a/avalanchego/snow/engine/avalanche/getter/getter_test.go b/avalanchego/snow/engine/avalanche/getter/getter_test.go index c0fcb70a..eb798399 100644 --- a/avalanchego/snow/engine/avalanche/getter/getter_test.go +++ b/avalanchego/snow/engine/avalanche/getter/getter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package getter diff --git a/avalanchego/snow/engine/avalanche/issuer.go b/avalanchego/snow/engine/avalanche/issuer.go index 1d38d83b..460a0170 100644 --- a/avalanchego/snow/engine/avalanche/issuer.go +++ b/avalanchego/snow/engine/avalanche/issuer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/avalanche/metrics.go b/avalanchego/snow/engine/avalanche/metrics.go index ccf8f6a4..939af937 100644 --- a/avalanchego/snow/engine/avalanche/metrics.go +++ b/avalanchego/snow/engine/avalanche/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/avalanche/mocks/engine.go b/avalanchego/snow/engine/avalanche/mocks/engine.go index d57b95a1..291f35ca 100644 --- a/avalanchego/snow/engine/avalanche/mocks/engine.go +++ b/avalanchego/snow/engine/avalanche/mocks/engine.go @@ -150,20 +150,6 @@ func (_m *Engine) Chits(validatorID ids.NodeID, requestID uint32, containerIDs [ return r0 } -// ChitsV2 provides a mock function with given fields: validatorID, requestID, containerIDs, containerID -func (_m *Engine) ChitsV2(validatorID ids.NodeID, requestID uint32, containerIDs []ids.ID, containerID ids.ID) error { - ret := _m.Called(validatorID, requestID, containerIDs, containerID) - - var r0 error - if rf, ok := ret.Get(0).(func(ids.NodeID, uint32, []ids.ID, ids.ID) error); ok { - r0 = rf(validatorID, requestID, containerIDs, containerID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // Connected provides a mock function with given fields: id, nodeVersion func (_m *Engine) Connected(id ids.NodeID, nodeVersion *version.Application) error { ret := _m.Called(id, nodeVersion) diff --git a/avalanchego/snow/engine/avalanche/state/prefixed_state.go b/avalanchego/snow/engine/avalanche/state/prefixed_state.go index bd962bb0..13a328ea 100644 --- a/avalanchego/snow/engine/avalanche/state/prefixed_state.go +++ b/avalanchego/snow/engine/avalanche/state/prefixed_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/snow/engine/avalanche/state/serializer.go b/avalanchego/snow/engine/avalanche/state/serializer.go index 478289e5..6468b073 100644 --- a/avalanchego/snow/engine/avalanche/state/serializer.go +++ b/avalanchego/snow/engine/avalanche/state/serializer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // Package state manages the meta-data required by consensus for an avalanche diff --git a/avalanchego/snow/engine/avalanche/state/state.go b/avalanchego/snow/engine/avalanche/state/state.go index 35479186..82c0aaeb 100644 --- a/avalanchego/snow/engine/avalanche/state/state.go +++ b/avalanchego/snow/engine/avalanche/state/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -143,14 +143,10 @@ func (s *state) SetEdge(id ids.ID, frontier []ids.ID) error { size := wrappers.IntLen + hashing.HashLen*len(frontier) p := wrappers.Packer{Bytes: make([]byte, size)} - p.PackInt(uint32(len(frontier))) for _, id := range frontier { p.PackFixedBytes(id[:]) } - s.log.AssertNoError(p.Err) - s.log.AssertTrue(p.Offset == len(p.Bytes), "Wrong offset after packing") - return s.db.Put(id[:], p.Bytes) } diff --git a/avalanchego/snow/engine/avalanche/state/unique_vertex.go b/avalanchego/snow/engine/avalanche/state/unique_vertex.go index 50fe5acd..791a9df7 100644 --- a/avalanchego/snow/engine/avalanche/state/unique_vertex.go +++ b/avalanchego/snow/engine/avalanche/state/unique_vertex.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/snow/engine/avalanche/state/unique_vertex_test.go b/avalanchego/snow/engine/avalanche/state/unique_vertex_test.go index bd05f174..959285cc 100644 --- a/avalanchego/snow/engine/avalanche/state/unique_vertex_test.go +++ b/avalanchego/snow/engine/avalanche/state/unique_vertex_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/snow/engine/avalanche/test_avalanche_engine.go b/avalanchego/snow/engine/avalanche/test_avalanche_engine.go index b3794d79..19e9dd07 100644 --- a/avalanchego/snow/engine/avalanche/test_avalanche_engine.go +++ b/avalanchego/snow/engine/avalanche/test_avalanche_engine.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/avalanche/transitive.go b/avalanchego/snow/engine/avalanche/transitive.go index dd790a8b..08a3416a 100644 --- a/avalanchego/snow/engine/avalanche/transitive.go +++ b/avalanchego/snow/engine/avalanche/transitive.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche @@ -114,7 +114,23 @@ func (t *Transitive) Put(nodeID ids.NodeID, requestID uint32, vtxBytes []byte) e return t.GetFailed(nodeID, requestID) } - if t.Consensus.VertexIssued(vtx) || t.pending.Contains(vtx.ID()) { + actualVtxID := vtx.ID() + expectedVtxID, ok := t.outstandingVtxReqs.Get(nodeID, requestID) + // If the provided vertex is not the requested vertex, we need to explicitly + // mark the request as failed to avoid having a dangling dependency. + if ok && actualVtxID != expectedVtxID { + t.Ctx.Log.Debug("incorrect vertex returned in Put", + zap.Stringer("nodeID", nodeID), + zap.Uint32("requestID", requestID), + zap.Stringer("vtxID", actualVtxID), + zap.Stringer("expectedVtxID", expectedVtxID), + ) + // We assume that [vtx] is useless because it doesn't match what we + // expected. + return t.GetFailed(nodeID, requestID) + } + + if t.Consensus.VertexIssued(vtx) || t.pending.Contains(actualVtxID) { t.metrics.numUselessPutBytes.Add(float64(len(vtxBytes))) } @@ -215,10 +231,6 @@ func (t *Transitive) Chits(nodeID ids.NodeID, requestID uint32, votes []ids.ID) return t.attemptToIssueTxs() } -func (t *Transitive) ChitsV2(nodeID ids.NodeID, requestID uint32, votes []ids.ID, _ ids.ID) error { - return t.Chits(nodeID, requestID, votes) -} - func (t *Transitive) QueryFailed(nodeID ids.NodeID, requestID uint32) error { return t.Chits(nodeID, requestID, nil) } @@ -281,7 +293,7 @@ func (t *Transitive) Gossip() error { t.Ctx.Log.Verbo("gossiping accepted vertex to the network", zap.Stringer("vtxID", vtxID), ) - t.Sender.SendGossip(vtxID, vtx.Bytes()) + t.Sender.SendGossip(vtx.Bytes()) return nil } @@ -305,7 +317,7 @@ func (t *Transitive) Notify(msg common.Message) error { default: t.Ctx.Log.Warn("received an unexpected message from the VM", - zap.Stringer("message", msg), + zap.Stringer("messageString", msg), ) return nil } diff --git a/avalanchego/snow/engine/avalanche/transitive_test.go b/avalanchego/snow/engine/avalanche/transitive_test.go index 3fe8ccae..4688d78b 100644 --- a/avalanchego/snow/engine/avalanche/transitive_test.go +++ b/avalanchego/snow/engine/avalanche/transitive_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche @@ -293,7 +293,7 @@ func TestEngineQuery(t *testing.T) { queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtx []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -304,7 +304,7 @@ func TestEngineQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if vtx0.ID() != vtxID { + if !bytes.Equal(vtx0.Bytes(), vtx) { t.Fatalf("Asking for wrong vertex") } } @@ -377,7 +377,7 @@ func TestEngineQuery(t *testing.T) { } *queried = false - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtx []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -388,7 +388,7 @@ func TestEngineQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if vtx1.ID() != vtxID { + if !bytes.Equal(vtx1.Bytes(), vtx) { t.Fatalf("Asking for wrong vertex") } } @@ -549,7 +549,7 @@ func TestEngineMultipleQuery(t *testing.T) { queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtx []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -560,7 +560,7 @@ func TestEngineMultipleQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if vtx0.ID() != vtxID { + if !bytes.Equal(vtx0.Bytes(), vtx) { t.Fatalf("Asking for wrong vertex") } } @@ -858,7 +858,7 @@ func TestEngineScheduleRepoll(t *testing.T) { } requestID := new(uint32) - sender.SendPushQueryF = func(_ ids.NodeIDSet, reqID uint32, _ ids.ID, _ []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, reqID uint32, _ []byte) { *requestID = reqID } @@ -1305,7 +1305,7 @@ func TestEngineReissue(t *testing.T) { } queryRequestID := new(uint32) - sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, _ ids.ID, _ []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, _ []byte) { *queryRequestID = requestID } @@ -1509,11 +1509,11 @@ func TestEngineGetVertex(t *testing.T) { t.Fatal(err) } - sender.SendPutF = func(v ids.NodeID, _ uint32, vtxID ids.ID, vtx []byte) { + sender.SendPutF = func(v ids.NodeID, _ uint32, vtx []byte) { if v != vdr.ID() { t.Fatalf("Wrong validator") } - if mVtx.ID() != vtxID { + if !bytes.Equal(mVtx.Bytes(), vtx) { t.Fatalf("Wrong vertex") } } @@ -1581,7 +1581,7 @@ func TestEngineInsufficientValidators(t *testing.T) { } queried := new(bool) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, _ uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(ids.NodeIDSet, uint32, []byte) { *queried = true } @@ -2068,14 +2068,14 @@ func TestEngineBlockingChitResponse(t *testing.T) { } queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtx []byte) { *queryRequestID = requestID vdrSet := ids.NodeIDSet{} vdrSet.Add(vdr) if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if issuedVtx.ID() != vtxID { + if !bytes.Equal(issuedVtx.Bytes(), vtx) { t.Fatalf("Asking for wrong vertex") } } @@ -2201,14 +2201,14 @@ func TestEngineMissingTx(t *testing.T) { } queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtx []byte) { *queryRequestID = requestID vdrSet := ids.NodeIDSet{} vdrSet.Add(vdr) if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if issuedVtx.ID() != vtxID { + if !bytes.Equal(issuedVtx.Bytes(), vtx) { t.Fatalf("Asking for wrong vertex") } } @@ -2684,7 +2684,7 @@ func TestEngineBootstrappingIntoConsensus(t *testing.T) { t.Fatalf("Returned wrong chits") } } - sender.SendPushQueryF = func(vdrs ids.NodeIDSet, _ uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(vdrs ids.NodeIDSet, _ uint32, vtx []byte) { if vdrs.Len() != 1 { t.Fatalf("Should have requested from the validators") } @@ -2692,9 +2692,6 @@ func TestEngineBootstrappingIntoConsensus(t *testing.T) { t.Fatalf("Should have requested from %s", vdr) } - if vtxID1 != vtxID { - t.Fatalf("Sent wrong query ID") - } if !bytes.Equal(vtxBytes1, vtx) { t.Fatalf("Sent wrong query bytes") } @@ -3134,7 +3131,7 @@ func TestEngineReBootstrappingIntoConsensus(t *testing.T) { t.Fatalf("Returned wrong chits") } } - sender.SendPushQueryF = func(vdrs ids.NodeIDSet, _ uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(vdrs ids.NodeIDSet, _ uint32, vtx []byte) { if vdrs.Len() != 1 { t.Fatalf("Should have requested from the validators") } @@ -3142,9 +3139,6 @@ func TestEngineReBootstrappingIntoConsensus(t *testing.T) { t.Fatalf("Should have requested from %s", vdr) } - if vtxID1 != vtxID { - t.Fatalf("Sent wrong query ID") - } if !bytes.Equal(vtxBytes1, vtx) { t.Fatalf("Sent wrong query bytes") } @@ -3236,7 +3230,7 @@ func TestEngineUndeclaredDependencyDeadlock(t *testing.T) { te.Sender = sender reqID := new(uint32) - sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, _ ids.ID, _ []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, _ []byte) { *reqID = requestID } @@ -3244,7 +3238,7 @@ func TestEngineUndeclaredDependencyDeadlock(t *testing.T) { t.Fatal(err) } - sender.SendPushQueryF = func(ids.NodeIDSet, uint32, ids.ID, []byte) { + sender.SendPushQueryF = func(ids.NodeIDSet, uint32, []byte) { t.Fatalf("should have failed verification") } @@ -3327,25 +3321,25 @@ func TestEnginePartiallyValidVertex(t *testing.T) { t.Fatal(err) } - expectedVtxID := ids.GenerateTestID() + expectedVtxBytes := []byte{1} manager.BuildVtxF = func(_ []ids.ID, txs []snowstorm.Tx) (avalanche.Vertex, error) { return &avalanche.TestVertex{ TestDecidable: choices.TestDecidable{ - IDV: expectedVtxID, + IDV: ids.GenerateTestID(), StatusV: choices.Processing, }, ParentsV: vts, HeightV: 1, TxsV: txs, - BytesV: []byte{1}, + BytesV: expectedVtxBytes, }, nil } sender := &common.SenderTest{T: t} te.Sender = sender - sender.SendPushQueryF = func(_ ids.NodeIDSet, _ uint32, vtxID ids.ID, _ []byte) { - if expectedVtxID != vtxID { + sender.SendPushQueryF = func(_ ids.NodeIDSet, _ uint32, vtx []byte) { + if !bytes.Equal(expectedVtxBytes, vtx) { t.Fatalf("wrong vertex queried") } } @@ -3389,11 +3383,8 @@ func TestEngineGossip(t *testing.T) { } called := new(bool) - sender.SendGossipF = func(vtxID ids.ID, vtxBytes []byte) { + sender.SendGossipF = func(vtxBytes []byte) { *called = true - if vtxID != gVtx.ID() { - t.Fatal(errUnknownVertex) - } if !bytes.Equal(vtxBytes, gVtx.Bytes()) { t.Fatal(errUnknownVertex) } @@ -3793,7 +3784,7 @@ func TestEngineAggressivePolling(t *testing.T) { } numPushQueries := new(int) - sender.SendPushQueryF = func(ids.NodeIDSet, uint32, ids.ID, []byte) { *numPushQueries++ } + sender.SendPushQueryF = func(ids.NodeIDSet, uint32, []byte) { *numPushQueries++ } numPullQueries := new(int) sender.SendPullQueryF = func(ids.NodeIDSet, uint32, ids.ID) { *numPullQueries++ } @@ -4004,12 +3995,12 @@ func TestEngineDoubleChit(t *testing.T) { } reqID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, _ []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxBytes []byte) { *reqID = requestID if inVdrs.Len() != 2 { t.Fatalf("Wrong number of validators") } - if vtxID != vtx.ID() { + if !bytes.Equal(vtx.Bytes(), vtxBytes) { t.Fatalf("Wrong vertex requested") } } @@ -4167,10 +4158,10 @@ func TestEngineBubbleVotes(t *testing.T) { queryReqID := new(uint32) queried := new(bool) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, _ []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxBytes []byte) { require.Len(t, inVdrs, 1, "wrong number of validators") *queryReqID = requestID - require.Equal(t, vtx.ID(), vtxID, "wrong vertex requested") + require.Equal(t, vtx.Bytes(), vtxBytes, "wrong vertex requested") *queried = true } @@ -4288,11 +4279,12 @@ func TestEngineIssue(t *testing.T) { vm.CantSetState = true numBuilt := 0 + vtxID := ids.GenerateTestID() manager.BuildVtxF = func(_ []ids.ID, txs []snowstorm.Tx) (avalanche.Vertex, error) { numBuilt++ vtx := &avalanche.TestVertex{ TestDecidable: choices.TestDecidable{ - IDV: ids.GenerateTestID(), + IDV: vtxID, StatusV: choices.Processing, }, ParentsV: []avalanche.Vertex{gVtx, mVtx}, @@ -4317,12 +4309,8 @@ func TestEngineIssue(t *testing.T) { return vtx, nil } - var ( - vtxID ids.ID - queryRequestID uint32 - ) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vID ids.ID, vtx []byte) { - vtxID = vID + var queryRequestID uint32 + sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, _ []byte) { queryRequestID = requestID } @@ -4580,11 +4568,10 @@ func TestSendMixedQuery(t *testing.T) { pushQuerySent := new(bool) pushQueryReqID := new(uint32) pushQueriedVdrs := ids.NodeIDSet{} - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, vtx []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, vtx []byte) { switch { case *pushQuerySent: t.Fatal("Asked multiple times") - case vtxID != vtx1.ID(): case !bytes.Equal(vtx, vtx1.Bytes()): t.Fatal("got unexpected block bytes instead of blk1") } @@ -4623,34 +4610,3 @@ func TestSendMixedQuery(t *testing.T) { }) } } - -func TestHandleChitsV2(t *testing.T) { - require := require.New(t) - _, _, engCfg := DefaultConfig() - - sender := &common.SenderTest{T: t} - - asked := new(bool) - sender.SendGetF = func(inVdr ids.NodeID, _ uint32, vtxID ids.ID) { - if *asked { - t.Fatal("asked multiple times") - } - *asked = true - } - - engCfg.Sender = sender - sender.Default(true) - manager := vertex.NewTestManager(t) - engCfg.Manager = manager - - reg := prometheus.NewRegistry() - engCfg.Ctx.Registerer = reg - - te, err := newTransitive(engCfg) - require.NoError(err) - require.NoError(te.Start(0)) - - // expects chits v2 to v1 conversion - require.NoError(te.ChitsV2(ids.GenerateTestNodeID(), 0, []ids.ID{ids.GenerateTestID()}, ids.Empty)) - require.True(*asked) -} diff --git a/avalanchego/snow/engine/avalanche/vertex/builder.go b/avalanchego/snow/engine/avalanche/vertex/builder.go index f727a2ab..d719ed84 100644 --- a/avalanchego/snow/engine/avalanche/vertex/builder.go +++ b/avalanchego/snow/engine/avalanche/vertex/builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/builder_test.go b/avalanchego/snow/engine/avalanche/vertex/builder_test.go index 64f143a0..25cee635 100644 --- a/avalanchego/snow/engine/avalanche/vertex/builder_test.go +++ b/avalanchego/snow/engine/avalanche/vertex/builder_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/codec.go b/avalanchego/snow/engine/avalanche/vertex/codec.go index c21976fc..203a713b 100644 --- a/avalanchego/snow/engine/avalanche/vertex/codec.go +++ b/avalanchego/snow/engine/avalanche/vertex/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/heap.go b/avalanchego/snow/engine/avalanche/vertex/heap.go index 842a38ca..4dbf4f66 100644 --- a/avalanchego/snow/engine/avalanche/vertex/heap.go +++ b/avalanchego/snow/engine/avalanche/vertex/heap.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/heap_test.go b/avalanchego/snow/engine/avalanche/vertex/heap_test.go index 52399a9f..b09fff3e 100644 --- a/avalanchego/snow/engine/avalanche/vertex/heap_test.go +++ b/avalanchego/snow/engine/avalanche/vertex/heap_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/manager.go b/avalanchego/snow/engine/avalanche/vertex/manager.go index c39c22eb..4da2d7ae 100644 --- a/avalanchego/snow/engine/avalanche/vertex/manager.go +++ b/avalanchego/snow/engine/avalanche/vertex/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/parser.go b/avalanchego/snow/engine/avalanche/vertex/parser.go index f26becbb..60409fcf 100644 --- a/avalanchego/snow/engine/avalanche/vertex/parser.go +++ b/avalanchego/snow/engine/avalanche/vertex/parser.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/parser_test.go b/avalanchego/snow/engine/avalanche/vertex/parser_test.go index 2fc5c106..fd2104c9 100644 --- a/avalanchego/snow/engine/avalanche/vertex/parser_test.go +++ b/avalanchego/snow/engine/avalanche/vertex/parser_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/sorting.go b/avalanchego/snow/engine/avalanche/vertex/sorting.go index f8fe242f..ed8f350c 100644 --- a/avalanchego/snow/engine/avalanche/vertex/sorting.go +++ b/avalanchego/snow/engine/avalanche/vertex/sorting.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/stateless_vertex.go b/avalanchego/snow/engine/avalanche/vertex/stateless_vertex.go index bac2dfd1..c4d688fe 100644 --- a/avalanchego/snow/engine/avalanche/vertex/stateless_vertex.go +++ b/avalanchego/snow/engine/avalanche/vertex/stateless_vertex.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/stateless_vertex_test.go b/avalanchego/snow/engine/avalanche/vertex/stateless_vertex_test.go index 856a9d02..c1d60a81 100644 --- a/avalanchego/snow/engine/avalanche/vertex/stateless_vertex_test.go +++ b/avalanchego/snow/engine/avalanche/vertex/stateless_vertex_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/storage.go b/avalanchego/snow/engine/avalanche/vertex/storage.go index 8758a52c..0a3ce368 100644 --- a/avalanchego/snow/engine/avalanche/vertex/storage.go +++ b/avalanchego/snow/engine/avalanche/vertex/storage.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/test_builder.go b/avalanchego/snow/engine/avalanche/vertex/test_builder.go index 6ad1bd8b..25ff4ce5 100644 --- a/avalanchego/snow/engine/avalanche/vertex/test_builder.go +++ b/avalanchego/snow/engine/avalanche/vertex/test_builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/test_manager.go b/avalanchego/snow/engine/avalanche/vertex/test_manager.go index 7aac4ab5..6d5fe33c 100644 --- a/avalanchego/snow/engine/avalanche/vertex/test_manager.go +++ b/avalanchego/snow/engine/avalanche/vertex/test_manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/test_parser.go b/avalanchego/snow/engine/avalanche/vertex/test_parser.go index 628b23c6..c7b97896 100644 --- a/avalanchego/snow/engine/avalanche/vertex/test_parser.go +++ b/avalanchego/snow/engine/avalanche/vertex/test_parser.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/test_storage.go b/avalanchego/snow/engine/avalanche/vertex/test_storage.go index f0f206e2..2ed40c53 100644 --- a/avalanchego/snow/engine/avalanche/vertex/test_storage.go +++ b/avalanchego/snow/engine/avalanche/vertex/test_storage.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/test_vm.go b/avalanchego/snow/engine/avalanche/vertex/test_vm.go index 502c66fc..17ef7cf6 100644 --- a/avalanchego/snow/engine/avalanche/vertex/test_vm.go +++ b/avalanchego/snow/engine/avalanche/vertex/test_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/vertex/vm.go b/avalanchego/snow/engine/avalanche/vertex/vm.go index 8e148bb4..52289419 100644 --- a/avalanchego/snow/engine/avalanche/vertex/vm.go +++ b/avalanchego/snow/engine/avalanche/vertex/vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vertex diff --git a/avalanchego/snow/engine/avalanche/voter.go b/avalanchego/snow/engine/avalanche/voter.go index 97589f2d..c0af5e6b 100644 --- a/avalanchego/snow/engine/avalanche/voter.go +++ b/avalanchego/snow/engine/avalanche/voter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/avalanche/voter_test.go b/avalanchego/snow/engine/avalanche/voter_test.go index 4cf12a01..94d14882 100644 --- a/avalanchego/snow/engine/avalanche/voter_test.go +++ b/avalanchego/snow/engine/avalanche/voter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avalanche diff --git a/avalanchego/snow/engine/common/appsender/appsender_client.go b/avalanchego/snow/engine/common/appsender/appsender_client.go index 28389520..f95ceb0b 100644 --- a/avalanchego/snow/engine/common/appsender/appsender_client.go +++ b/avalanchego/snow/engine/common/appsender/appsender_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package appsender diff --git a/avalanchego/snow/engine/common/appsender/appsender_server.go b/avalanchego/snow/engine/common/appsender/appsender_server.go index a13e4f62..03267026 100644 --- a/avalanchego/snow/engine/common/appsender/appsender_server.go +++ b/avalanchego/snow/engine/common/appsender/appsender_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package appsender diff --git a/avalanchego/snow/engine/common/bootstrapable.go b/avalanchego/snow/engine/common/bootstrapable.go index 1da36844..0636b785 100644 --- a/avalanchego/snow/engine/common/bootstrapable.go +++ b/avalanchego/snow/engine/common/bootstrapable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/bootstrapper.go b/avalanchego/snow/engine/common/bootstrapper.go index b98f811a..57363e9e 100644 --- a/avalanchego/snow/engine/common/bootstrapper.go +++ b/avalanchego/snow/engine/common/bootstrapper.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/config.go b/avalanchego/snow/engine/common/config.go index 433ecf47..803bf736 100644 --- a/avalanchego/snow/engine/common/config.go +++ b/avalanchego/snow/engine/common/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/engine.go b/avalanchego/snow/engine/common/engine.go index bb937ac5..97c4e919 100644 --- a/avalanchego/snow/engine/common/engine.go +++ b/avalanchego/snow/engine/common/engine.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common @@ -380,17 +380,6 @@ type ChitsHandler interface { // However, the validatorID is assumed to be authenticated. Chits(validatorID ids.NodeID, requestID uint32, containerIDs []ids.ID) error - // Notify this engine of the specified validators preferences. - // - // This function can be called by any validator. It is not safe to assume - // this message is in response to a PullQuery or a PushQuery message. - // However, the validatorID is assumed to be authenticated. - // - // The new ChitsV2 could be sent from a node that implements "ChitsV2" - // sender with linearized DAG, while the older nodes may not have - // implemented such, thus fallback. - ChitsV2(validatorID ids.NodeID, requestID uint32, containerIDs []ids.ID, containerID ids.ID) error - // Notify this engine that a query it issued has failed. // // This function will be called if the engine sent a PullQuery or PushQuery diff --git a/avalanchego/snow/engine/common/fetcher.go b/avalanchego/snow/engine/common/fetcher.go index b0c94554..8084023d 100644 --- a/avalanchego/snow/engine/common/fetcher.go +++ b/avalanchego/snow/engine/common/fetcher.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/fx.go b/avalanchego/snow/engine/common/fx.go index 0c1ddd76..cfac66c1 100644 --- a/avalanchego/snow/engine/common/fx.go +++ b/avalanchego/snow/engine/common/fx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/halter.go b/avalanchego/snow/engine/common/halter.go index 33c415ac..f67eb3c7 100644 --- a/avalanchego/snow/engine/common/halter.go +++ b/avalanchego/snow/engine/common/halter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/http_handler.go b/avalanchego/snow/engine/common/http_handler.go index ae404615..35ed954c 100644 --- a/avalanchego/snow/engine/common/http_handler.go +++ b/avalanchego/snow/engine/common/http_handler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/message.go b/avalanchego/snow/engine/common/message.go index 432aab9f..7209ea8b 100644 --- a/avalanchego/snow/engine/common/message.go +++ b/avalanchego/snow/engine/common/message.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/mixed_query.go b/avalanchego/snow/engine/common/mixed_query.go index 59e361d4..56a259dc 100644 --- a/avalanchego/snow/engine/common/mixed_query.go +++ b/avalanchego/snow/engine/common/mixed_query.go @@ -25,7 +25,7 @@ func SendMixedQuery( if numPushTo > 0 { sendPushQueryTo := ids.NewNodeIDSet(numPushTo) sendPushQueryTo.Add(vdrs[:numPushTo]...) - sender.SendPushQuery(sendPushQueryTo, reqID, containerID, container) + sender.SendPushQuery(sendPushQueryTo, reqID, container) } if numPullTo := len(vdrs) - numPushTo; numPullTo > 0 { sendPullQueryTo := ids.NewNodeIDSet(numPullTo) diff --git a/avalanchego/snow/engine/common/mixed_query_test.go b/avalanchego/snow/engine/common/mixed_query_test.go index e1946452..8147e790 100644 --- a/avalanchego/snow/engine/common/mixed_query_test.go +++ b/avalanchego/snow/engine/common/mixed_query_test.go @@ -31,7 +31,6 @@ func TestSendMixedQuery(t *testing.T) { s.EXPECT().SendPushQuery( ids.NodeIDSet{vdr1: struct{}{}, vdr2: struct{}{}, vdr3: struct{}{}}, reqID, - containerID, containerBytes, ).Times(1) s.EXPECT().SendPullQuery( @@ -50,7 +49,6 @@ func TestSendMixedQuery(t *testing.T) { s.EXPECT().SendPushQuery( ids.NodeIDSet{vdr1: struct{}{}}, reqID, - containerID, containerBytes, ).Times(1) s.EXPECT().SendPullQuery( @@ -69,7 +67,6 @@ func TestSendMixedQuery(t *testing.T) { s.EXPECT().SendPushQuery( ids.NodeIDSet{vdr1: struct{}{}, vdr2: struct{}{}}, reqID, - containerID, containerBytes, ).Times(1) s.EXPECT().SendPullQuery( @@ -88,7 +85,6 @@ func TestSendMixedQuery(t *testing.T) { s.EXPECT().SendPushQuery( gomock.Any(), reqID, - containerID, containerBytes, ).Times(0) s.EXPECT().SendPullQuery( @@ -107,7 +103,6 @@ func TestSendMixedQuery(t *testing.T) { s.EXPECT().SendPushQuery( ids.NodeIDSet{vdr1: struct{}{}, vdr2: struct{}{}}, reqID, - containerID, containerBytes, ).Times(1) s.EXPECT().SendPullQuery( diff --git a/avalanchego/snow/engine/common/mock_sender.go b/avalanchego/snow/engine/common/mock_sender.go index a296ba66..abab0b14 100644 --- a/avalanchego/snow/engine/common/mock_sender.go +++ b/avalanchego/snow/engine/common/mock_sender.go @@ -164,18 +164,6 @@ func (mr *MockSenderMockRecorder) SendChits(nodeID, requestID, votes interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendChits", reflect.TypeOf((*MockSender)(nil).SendChits), nodeID, requestID, votes) } -// SendChitsV2 mocks base method. -func (m *MockSender) SendChitsV2(nodeID ids.NodeID, requestID uint32, votes []ids.ID, vote ids.ID) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SendChitsV2", nodeID, requestID, votes, vote) -} - -// SendChitsV2 indicates an expected call of SendChitsV2. -func (mr *MockSenderMockRecorder) SendChitsV2(nodeID, requestID, votes, vote interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendChitsV2", reflect.TypeOf((*MockSender)(nil).SendChitsV2), nodeID, requestID, votes, vote) -} - // SendGet mocks base method. func (m *MockSender) SendGet(nodeID ids.NodeID, requestID uint32, containerID ids.ID) { m.ctrl.T.Helper() @@ -249,15 +237,15 @@ func (mr *MockSenderMockRecorder) SendGetStateSummaryFrontier(nodeIDs, requestID } // SendGossip mocks base method. -func (m *MockSender) SendGossip(containerID ids.ID, container []byte) { +func (m *MockSender) SendGossip(container []byte) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SendGossip", containerID, container) + m.ctrl.Call(m, "SendGossip", container) } // SendGossip indicates an expected call of SendGossip. -func (mr *MockSenderMockRecorder) SendGossip(containerID, container interface{}) *gomock.Call { +func (mr *MockSenderMockRecorder) SendGossip(container interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendGossip", reflect.TypeOf((*MockSender)(nil).SendGossip), containerID, container) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendGossip", reflect.TypeOf((*MockSender)(nil).SendGossip), container) } // SendPullQuery mocks base method. @@ -273,27 +261,27 @@ func (mr *MockSenderMockRecorder) SendPullQuery(nodeIDs, requestID, containerID } // SendPushQuery mocks base method. -func (m *MockSender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, containerID ids.ID, container []byte) { +func (m *MockSender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, container []byte) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SendPushQuery", nodeIDs, requestID, containerID, container) + m.ctrl.Call(m, "SendPushQuery", nodeIDs, requestID, container) } // SendPushQuery indicates an expected call of SendPushQuery. -func (mr *MockSenderMockRecorder) SendPushQuery(nodeIDs, requestID, containerID, container interface{}) *gomock.Call { +func (mr *MockSenderMockRecorder) SendPushQuery(nodeIDs, requestID, container interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPushQuery", reflect.TypeOf((*MockSender)(nil).SendPushQuery), nodeIDs, requestID, containerID, container) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPushQuery", reflect.TypeOf((*MockSender)(nil).SendPushQuery), nodeIDs, requestID, container) } // SendPut mocks base method. -func (m *MockSender) SendPut(nodeID ids.NodeID, requestID uint32, containerID ids.ID, container []byte) { +func (m *MockSender) SendPut(nodeID ids.NodeID, requestID uint32, container []byte) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SendPut", nodeID, requestID, containerID, container) + m.ctrl.Call(m, "SendPut", nodeID, requestID, container) } // SendPut indicates an expected call of SendPut. -func (mr *MockSenderMockRecorder) SendPut(nodeID, requestID, containerID, container interface{}) *gomock.Call { +func (mr *MockSenderMockRecorder) SendPut(nodeID, requestID, container interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPut", reflect.TypeOf((*MockSender)(nil).SendPut), nodeID, requestID, containerID, container) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPut", reflect.TypeOf((*MockSender)(nil).SendPut), nodeID, requestID, container) } // SendStateSummaryFrontier mocks base method. @@ -556,15 +544,15 @@ func (mr *MockFetchSenderMockRecorder) SendGetAncestors(nodeID, requestID, conta } // SendPut mocks base method. -func (m *MockFetchSender) SendPut(nodeID ids.NodeID, requestID uint32, containerID ids.ID, container []byte) { +func (m *MockFetchSender) SendPut(nodeID ids.NodeID, requestID uint32, container []byte) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SendPut", nodeID, requestID, containerID, container) + m.ctrl.Call(m, "SendPut", nodeID, requestID, container) } // SendPut indicates an expected call of SendPut. -func (mr *MockFetchSenderMockRecorder) SendPut(nodeID, requestID, containerID, container interface{}) *gomock.Call { +func (mr *MockFetchSenderMockRecorder) SendPut(nodeID, requestID, container interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPut", reflect.TypeOf((*MockFetchSender)(nil).SendPut), nodeID, requestID, containerID, container) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPut", reflect.TypeOf((*MockFetchSender)(nil).SendPut), nodeID, requestID, container) } // MockQuerySender is a mock of QuerySender interface. @@ -602,18 +590,6 @@ func (mr *MockQuerySenderMockRecorder) SendChits(nodeID, requestID, votes interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendChits", reflect.TypeOf((*MockQuerySender)(nil).SendChits), nodeID, requestID, votes) } -// SendChitsV2 mocks base method. -func (m *MockQuerySender) SendChitsV2(nodeID ids.NodeID, requestID uint32, votes []ids.ID, vote ids.ID) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SendChitsV2", nodeID, requestID, votes, vote) -} - -// SendChitsV2 indicates an expected call of SendChitsV2. -func (mr *MockQuerySenderMockRecorder) SendChitsV2(nodeID, requestID, votes, vote interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendChitsV2", reflect.TypeOf((*MockQuerySender)(nil).SendChitsV2), nodeID, requestID, votes, vote) -} - // SendPullQuery mocks base method. func (m *MockQuerySender) SendPullQuery(nodeIDs ids.NodeIDSet, requestID uint32, containerID ids.ID) { m.ctrl.T.Helper() @@ -627,15 +603,15 @@ func (mr *MockQuerySenderMockRecorder) SendPullQuery(nodeIDs, requestID, contain } // SendPushQuery mocks base method. -func (m *MockQuerySender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, containerID ids.ID, container []byte) { +func (m *MockQuerySender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, container []byte) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SendPushQuery", nodeIDs, requestID, containerID, container) + m.ctrl.Call(m, "SendPushQuery", nodeIDs, requestID, container) } // SendPushQuery indicates an expected call of SendPushQuery. -func (mr *MockQuerySenderMockRecorder) SendPushQuery(nodeIDs, requestID, containerID, container interface{}) *gomock.Call { +func (mr *MockQuerySenderMockRecorder) SendPushQuery(nodeIDs, requestID, container interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPushQuery", reflect.TypeOf((*MockQuerySender)(nil).SendPushQuery), nodeIDs, requestID, containerID, container) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPushQuery", reflect.TypeOf((*MockQuerySender)(nil).SendPushQuery), nodeIDs, requestID, container) } // MockGossiper is a mock of Gossiper interface. @@ -662,15 +638,15 @@ func (m *MockGossiper) EXPECT() *MockGossiperMockRecorder { } // SendGossip mocks base method. -func (m *MockGossiper) SendGossip(containerID ids.ID, container []byte) { +func (m *MockGossiper) SendGossip(container []byte) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SendGossip", containerID, container) + m.ctrl.Call(m, "SendGossip", container) } // SendGossip indicates an expected call of SendGossip. -func (mr *MockGossiperMockRecorder) SendGossip(containerID, container interface{}) *gomock.Call { +func (mr *MockGossiperMockRecorder) SendGossip(container interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendGossip", reflect.TypeOf((*MockGossiper)(nil).SendGossip), containerID, container) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendGossip", reflect.TypeOf((*MockGossiper)(nil).SendGossip), container) } // MockAppSender is a mock of AppSender interface. diff --git a/avalanchego/snow/engine/common/no_ops_handlers.go b/avalanchego/snow/engine/common/no_ops_handlers.go index dfff747b..501cac99 100644 --- a/avalanchego/snow/engine/common/no_ops_handlers.go +++ b/avalanchego/snow/engine/common/no_ops_handlers.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common @@ -249,16 +249,6 @@ func (nop *noOpChitsHandler) Chits(nodeID ids.NodeID, requestID uint32, _ []ids. return nil } -func (nop *noOpChitsHandler) ChitsV2(nodeID ids.NodeID, requestID uint32, _ []ids.ID, _ ids.ID) error { - nop.log.Debug("dropping request", - zap.String("reason", "unhandled by this gear"), - zap.Stringer("messageOp", message.ChitsV2), - zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", requestID), - ) - return nil -} - func (nop *noOpChitsHandler) QueryFailed(nodeID ids.NodeID, requestID uint32) error { nop.log.Debug("dropping request", zap.String("reason", "unhandled by this gear"), diff --git a/avalanchego/snow/engine/common/queue/job.go b/avalanchego/snow/engine/common/queue/job.go index 7243fdba..5f8f61d3 100644 --- a/avalanchego/snow/engine/common/queue/job.go +++ b/avalanchego/snow/engine/common/queue/job.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package queue diff --git a/avalanchego/snow/engine/common/queue/jobs.go b/avalanchego/snow/engine/common/queue/jobs.go index f6be2f0e..621911d2 100644 --- a/avalanchego/snow/engine/common/queue/jobs.go +++ b/avalanchego/snow/engine/common/queue/jobs.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package queue @@ -201,9 +201,11 @@ func (j *Jobs) ExecuteAll(ctx *snow.ConsensusContext, halter common.Haltable, re ) } } - j.etaMetric.Set(0) } + // Now that executing has finished, zero out the ETA. + j.etaMetric.Set(0) + if !restarted { ctx.Log.Info("executed operations", zap.Int("numExecuted", numExecuted), diff --git a/avalanchego/snow/engine/common/queue/jobs_test.go b/avalanchego/snow/engine/common/queue/jobs_test.go index e862c0b9..0ef9f295 100644 --- a/avalanchego/snow/engine/common/queue/jobs_test.go +++ b/avalanchego/snow/engine/common/queue/jobs_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package queue diff --git a/avalanchego/snow/engine/common/queue/parser.go b/avalanchego/snow/engine/common/queue/parser.go index 434c62e8..b808b2f3 100644 --- a/avalanchego/snow/engine/common/queue/parser.go +++ b/avalanchego/snow/engine/common/queue/parser.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package queue diff --git a/avalanchego/snow/engine/common/queue/state.go b/avalanchego/snow/engine/common/queue/state.go index 5f429936..01e22896 100644 --- a/avalanchego/snow/engine/common/queue/state.go +++ b/avalanchego/snow/engine/common/queue/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package queue diff --git a/avalanchego/snow/engine/common/queue/test_job.go b/avalanchego/snow/engine/common/queue/test_job.go index 8325711e..83abd2ec 100644 --- a/avalanchego/snow/engine/common/queue/test_job.go +++ b/avalanchego/snow/engine/common/queue/test_job.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package queue diff --git a/avalanchego/snow/engine/common/queue/test_parser.go b/avalanchego/snow/engine/common/queue/test_parser.go index 21ac2e24..b28a288a 100644 --- a/avalanchego/snow/engine/common/queue/test_parser.go +++ b/avalanchego/snow/engine/common/queue/test_parser.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package queue diff --git a/avalanchego/snow/engine/common/requests.go b/avalanchego/snow/engine/common/requests.go index a73005da..dbf64c25 100644 --- a/avalanchego/snow/engine/common/requests.go +++ b/avalanchego/snow/engine/common/requests.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common @@ -47,14 +47,17 @@ func (r *Requests) Add(vdr ids.NodeID, requestID uint32, containerID ids.ID) { } } +// Get the containerID the request is expecting and if the request exists. +func (r *Requests) Get(vdr ids.NodeID, requestID uint32) (ids.ID, bool) { + containerID, ok := r.reqsToID[vdr][requestID] + return containerID, ok +} + // Remove attempts to abandon a requestID sent to a validator. If the request is // currently outstanding, the requested ID will be returned along with true. If // the request isn't currently outstanding, false will be returned. func (r *Requests) Remove(vdr ids.NodeID, requestID uint32) (ids.ID, bool) { - vdrReqs, ok := r.reqsToID[vdr] - if !ok { - return ids.ID{}, false - } + vdrReqs := r.reqsToID[vdr] containerID, ok := vdrReqs[requestID] if !ok { return ids.ID{}, false diff --git a/avalanchego/snow/engine/common/requests_test.go b/avalanchego/snow/engine/common/requests_test.go index fec57eef..7984a7f8 100644 --- a/avalanchego/snow/engine/common/requests_test.go +++ b/avalanchego/snow/engine/common/requests_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/sender.go b/avalanchego/snow/engine/common/sender.go index 0689f5eb..2ae5ce91 100644 --- a/avalanchego/snow/engine/common/sender.go +++ b/avalanchego/snow/engine/common/sender.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common @@ -90,14 +90,8 @@ type FetchSender interface { // and its ancestors. SendGetAncestors(nodeID ids.NodeID, requestID uint32, containerID ids.ID) - // Tell the specified node that the container whose ID is [containerID] has - // body [container]. - SendPut( - nodeID ids.NodeID, - requestID uint32, - containerID ids.ID, - container []byte, - ) + // Tell the specified node about [container]. + SendPut(nodeID ids.NodeID, requestID uint32, container []byte) // Give the specified node several containers at once. Should be in response // to a GetAncestors message with request ID [requestID] from the node. @@ -109,14 +103,9 @@ type FetchSender interface { type QuerySender interface { // Request from the specified nodes their preferred frontier, given the // existence of the specified container. - // This is the same as PullQuery, except that this message includes not only - // the ID of the container but also its body. - SendPushQuery( - nodeIDs ids.NodeIDSet, - requestID uint32, - containerID ids.ID, - container []byte, - ) + // This is the same as PullQuery, except that this message includes the body + // of the container rather than its ID. + SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, container []byte) // Request from the specified nodes their preferred frontier, given the // existence of the specified container. @@ -124,16 +113,13 @@ type QuerySender interface { // Send chits to the specified node SendChits(nodeID ids.NodeID, requestID uint32, votes []ids.ID) - - // Send chits v2 to the specified node - SendChitsV2(nodeID ids.NodeID, requestID uint32, votes []ids.ID, vote ids.ID) } // Gossiper defines how a consensus engine gossips a container on the accepted // frontier to other nodes type Gossiper interface { // Gossip the provided container throughout the network - SendGossip(containerID ids.ID, container []byte) + SendGossip(container []byte) } // AppSender sends application (VM) level messages. diff --git a/avalanchego/snow/engine/common/state_syncer.go b/avalanchego/snow/engine/common/state_syncer.go index 2e65dc06..47bd1234 100644 --- a/avalanchego/snow/engine/common/state_syncer.go +++ b/avalanchego/snow/engine/common/state_syncer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/subnet.go b/avalanchego/snow/engine/common/subnet.go index b04bd745..7f75ab53 100644 --- a/avalanchego/snow/engine/common/subnet.go +++ b/avalanchego/snow/engine/common/subnet.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/subnet_tracker.go b/avalanchego/snow/engine/common/subnet_tracker.go index f9af4b67..6708feb1 100644 --- a/avalanchego/snow/engine/common/subnet_tracker.go +++ b/avalanchego/snow/engine/common/subnet_tracker.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common @@ -8,7 +8,7 @@ import ( ) // SubnetTracker describes the interface for checking if a node is tracking a -// subnet +// subnet, namely if a node has whitelisted a subnet. type SubnetTracker interface { // TracksSubnet returns true if [nodeID] tracks [subnetID] TracksSubnet(nodeID ids.NodeID, subnetID ids.ID) bool diff --git a/avalanchego/snow/engine/common/test_bootstrapable.go b/avalanchego/snow/engine/common/test_bootstrapable.go index e999de75..9be078eb 100644 --- a/avalanchego/snow/engine/common/test_bootstrapable.go +++ b/avalanchego/snow/engine/common/test_bootstrapable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/test_bootstrapper.go b/avalanchego/snow/engine/common/test_bootstrapper.go index 0d1ac53e..79b7fb90 100644 --- a/avalanchego/snow/engine/common/test_bootstrapper.go +++ b/avalanchego/snow/engine/common/test_bootstrapper.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/test_config.go b/avalanchego/snow/engine/common/test_config.go index d965bb5a..e6b4304a 100644 --- a/avalanchego/snow/engine/common/test_config.go +++ b/avalanchego/snow/engine/common/test_config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/test_engine.go b/avalanchego/snow/engine/common/test_engine.go index dc98409f..81cc8937 100644 --- a/avalanchego/snow/engine/common/test_engine.go +++ b/avalanchego/snow/engine/common/test_engine.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common @@ -39,7 +39,6 @@ var ( errPullQuery = errors.New("unexpectedly called PullQuery") errQueryFailed = errors.New("unexpectedly called QueryFailed") errChits = errors.New("unexpectedly called Chits") - errChitsV2 = errors.New("unexpectedly called ChitsV2") errStart = errors.New("unexpectedly called Start") _ Engine = &EngineTest{} @@ -88,7 +87,6 @@ type EngineTest struct { CantPullQuery, CantQueryFailed, CantChits, - CantChitsV2, CantConnected, CantDisconnected, @@ -112,7 +110,6 @@ type EngineTest struct { PutF, PushQueryF func(nodeID ids.NodeID, requestID uint32, container []byte) error AncestorsF func(nodeID ids.NodeID, requestID uint32, containers [][]byte) error AcceptedFrontierF, GetAcceptedF, AcceptedF, ChitsF func(nodeID ids.NodeID, requestID uint32, containerIDs []ids.ID) error - ChitsV2F func(nodeID ids.NodeID, requestID uint32, containerIDs []ids.ID, containerID ids.ID) error GetStateSummaryFrontierF, GetStateSummaryFrontierFailedF, GetAcceptedStateSummaryFailedF, GetAcceptedFrontierF, GetFailedF, GetAncestorsFailedF, QueryFailedF, GetAcceptedFrontierFailedF, GetAcceptedFailedF, AppRequestFailedF func(nodeID ids.NodeID, requestID uint32) error @@ -568,19 +565,6 @@ func (e *EngineTest) Chits(nodeID ids.NodeID, requestID uint32, containerIDs []i return errChits } -func (e *EngineTest) ChitsV2(nodeID ids.NodeID, requestID uint32, containerIDs []ids.ID, containerID ids.ID) error { - if e.ChitsV2F != nil { - return e.ChitsV2F(nodeID, requestID, containerIDs, containerID) - } - if !e.CantChitsV2 { - return nil - } - if e.T != nil { - e.T.Fatal(errChitsV2) - } - return errChitsV2 -} - func (e *EngineTest) Connected(nodeID ids.NodeID, nodeVersion *version.Application) error { if e.ConnectedF != nil { return e.ConnectedF(nodeID, nodeVersion) diff --git a/avalanchego/snow/engine/common/test_sender.go b/avalanchego/snow/engine/common/test_sender.go index d46d66e1..cb03619f 100644 --- a/avalanchego/snow/engine/common/test_sender.go +++ b/avalanchego/snow/engine/common/test_sender.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common @@ -31,7 +31,7 @@ type SenderTest struct { CantSendGetAcceptedFrontier, CantSendAcceptedFrontier, CantSendGetAccepted, CantSendAccepted, CantSendGet, CantSendGetAncestors, CantSendPut, CantSendAncestors, - CantSendPullQuery, CantSendPushQuery, CantSendChits, CantSendChitsV2, + CantSendPullQuery, CantSendPushQuery, CantSendChits, CantSendGossip, CantSendAppRequest, CantSendAppResponse, CantSendAppGossip, CantSendAppGossipSpecific bool @@ -46,13 +46,12 @@ type SenderTest struct { SendAcceptedF func(ids.NodeID, uint32, []ids.ID) SendGetF func(ids.NodeID, uint32, ids.ID) SendGetAncestorsF func(ids.NodeID, uint32, ids.ID) - SendPutF func(ids.NodeID, uint32, ids.ID, []byte) + SendPutF func(ids.NodeID, uint32, []byte) SendAncestorsF func(ids.NodeID, uint32, [][]byte) - SendPushQueryF func(ids.NodeIDSet, uint32, ids.ID, []byte) + SendPushQueryF func(ids.NodeIDSet, uint32, []byte) SendPullQueryF func(ids.NodeIDSet, uint32, ids.ID) SendChitsF func(ids.NodeID, uint32, []ids.ID) - SendChitsV2F func(ids.NodeID, uint32, []ids.ID, ids.ID) - SendGossipF func(ids.ID, []byte) + SendGossipF func([]byte) SendAppRequestF func(ids.NodeIDSet, uint32, []byte) error SendAppResponseF func(ids.NodeID, uint32, []byte) error SendAppGossipF func([]byte) error @@ -213,9 +212,9 @@ func (s *SenderTest) SendGetAncestors(validatorID ids.NodeID, requestID uint32, // SendPut calls SendPutF if it was initialized. If it wasn't initialized and // this function shouldn't be called and testing was initialized, then testing // will fail. -func (s *SenderTest) SendPut(vdr ids.NodeID, requestID uint32, vtxID ids.ID, vtx []byte) { +func (s *SenderTest) SendPut(vdr ids.NodeID, requestID uint32, vtx []byte) { if s.SendPutF != nil { - s.SendPutF(vdr, requestID, vtxID, vtx) + s.SendPutF(vdr, requestID, vtx) } else if s.CantSendPut && s.T != nil { s.T.Fatalf("Unexpectedly called SendPut") } @@ -235,9 +234,9 @@ func (s *SenderTest) SendAncestors(vdr ids.NodeID, requestID uint32, vtxs [][]by // SendPushQuery calls SendPushQueryF if it was initialized. If it wasn't // initialized and this function shouldn't be called and testing was // initialized, then testing will fail. -func (s *SenderTest) SendPushQuery(vdrs ids.NodeIDSet, requestID uint32, vtxID ids.ID, vtx []byte) { +func (s *SenderTest) SendPushQuery(vdrs ids.NodeIDSet, requestID uint32, vtx []byte) { if s.SendPushQueryF != nil { - s.SendPushQueryF(vdrs, requestID, vtxID, vtx) + s.SendPushQueryF(vdrs, requestID, vtx) } else if s.CantSendPushQuery && s.T != nil { s.T.Fatalf("Unexpectedly called SendPushQuery") } @@ -265,23 +264,12 @@ func (s *SenderTest) SendChits(vdr ids.NodeID, requestID uint32, votes []ids.ID) } } -// SendChitsV2 calls SendChitsV2F if it was initialized. If it wasn't initialized -// and this function shouldn't be called and testing was initialized, then -// testing will fail. -func (s *SenderTest) SendChitsV2(vdr ids.NodeID, requestID uint32, votes []ids.ID, vote ids.ID) { - if s.SendChitsV2F != nil { - s.SendChitsV2F(vdr, requestID, votes, vote) - } else if s.CantSendChitsV2 && s.T != nil { - s.T.Fatalf("Unexpectedly called SendChitsV2") - } -} - // SendGossip calls SendGossipF if it was initialized. If it wasn't initialized // and this function shouldn't be called and testing was initialized, then // testing will fail. -func (s *SenderTest) SendGossip(containerID ids.ID, container []byte) { +func (s *SenderTest) SendGossip(container []byte) { if s.SendGossipF != nil { - s.SendGossipF(containerID, container) + s.SendGossipF(container) } else if s.CantSendGossip && s.T != nil { s.T.Fatalf("Unexpectedly called SendGossip") } diff --git a/avalanchego/snow/engine/common/test_subnet.go b/avalanchego/snow/engine/common/test_subnet.go index 852bd0e3..5d220811 100644 --- a/avalanchego/snow/engine/common/test_subnet.go +++ b/avalanchego/snow/engine/common/test_subnet.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/test_timer.go b/avalanchego/snow/engine/common/test_timer.go index 5572ed71..9d307f5f 100644 --- a/avalanchego/snow/engine/common/test_timer.go +++ b/avalanchego/snow/engine/common/test_timer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/test_vm.go b/avalanchego/snow/engine/common/test_vm.go index 64e95fdd..31469d4e 100644 --- a/avalanchego/snow/engine/common/test_vm.go +++ b/avalanchego/snow/engine/common/test_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/timer.go b/avalanchego/snow/engine/common/timer.go index 930ceab7..980fa551 100644 --- a/avalanchego/snow/engine/common/timer.go +++ b/avalanchego/snow/engine/common/timer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/common/tracker/peers.go b/avalanchego/snow/engine/common/tracker/peers.go index 033f80a3..07f636c9 100644 --- a/avalanchego/snow/engine/common/tracker/peers.go +++ b/avalanchego/snow/engine/common/tracker/peers.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package tracker diff --git a/avalanchego/snow/engine/common/tracker/startup.go b/avalanchego/snow/engine/common/tracker/startup.go index b770f851..66891219 100644 --- a/avalanchego/snow/engine/common/tracker/startup.go +++ b/avalanchego/snow/engine/common/tracker/startup.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package tracker diff --git a/avalanchego/snow/engine/common/vm.go b/avalanchego/snow/engine/common/vm.go index 309214be..4c45ee0c 100644 --- a/avalanchego/snow/engine/common/vm.go +++ b/avalanchego/snow/engine/common/vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/snow/engine/snowman/ancestor_tree.go b/avalanchego/snow/engine/snowman/ancestor_tree.go index 43929190..92f43e41 100644 --- a/avalanchego/snow/engine/snowman/ancestor_tree.go +++ b/avalanchego/snow/engine/snowman/ancestor_tree.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/ancestor_tree_test.go b/avalanchego/snow/engine/snowman/ancestor_tree_test.go index 5f0dabfa..790a5d6d 100644 --- a/avalanchego/snow/engine/snowman/ancestor_tree_test.go +++ b/avalanchego/snow/engine/snowman/ancestor_tree_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/block/batched_vm.go b/avalanchego/snow/engine/snowman/block/batched_vm.go index a6acacca..921bc0e8 100644 --- a/avalanchego/snow/engine/snowman/block/batched_vm.go +++ b/avalanchego/snow/engine/snowman/block/batched_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/batched_vm_test.go b/avalanchego/snow/engine/snowman/block/batched_vm_test.go index 0eac3342..5193f226 100644 --- a/avalanchego/snow/engine/snowman/block/batched_vm_test.go +++ b/avalanchego/snow/engine/snowman/block/batched_vm_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/height_indexed_vm.go b/avalanchego/snow/engine/snowman/block/height_indexed_vm.go index d8f3a38b..724dc98a 100644 --- a/avalanchego/snow/engine/snowman/block/height_indexed_vm.go +++ b/avalanchego/snow/engine/snowman/block/height_indexed_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block @@ -23,7 +23,12 @@ type HeightIndexedChainVM interface { // index. VerifyHeightIndex() error - // GetBlockIDAtHeight returns the ID of the block that was accepted with - // [height]. + // GetBlockIDAtHeight returns: + // - The ID of the block that was accepted with [height]. + // - database.ErrNotFound if the [height] index is unknown. + // + // Note: A returned value of [database.ErrNotFound] typically means that the + // underlying VM was state synced and does not have access to the + // blockID at [height]. GetBlockIDAtHeight(height uint64) (ids.ID, error) } diff --git a/avalanchego/snow/engine/snowman/block/state_summary.go b/avalanchego/snow/engine/snowman/block/state_summary.go index 4382dcff..60fd54b9 100644 --- a/avalanchego/snow/engine/snowman/block/state_summary.go +++ b/avalanchego/snow/engine/snowman/block/state_summary.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/state_syncable_vm.go b/avalanchego/snow/engine/snowman/block/state_syncable_vm.go index c2c5b290..8e4e8b95 100644 --- a/avalanchego/snow/engine/snowman/block/state_syncable_vm.go +++ b/avalanchego/snow/engine/snowman/block/state_syncable_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/test_batched_vm.go b/avalanchego/snow/engine/snowman/block/test_batched_vm.go index 6bf098a3..dca36b37 100644 --- a/avalanchego/snow/engine/snowman/block/test_batched_vm.go +++ b/avalanchego/snow/engine/snowman/block/test_batched_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/test_height_indexed_vm.go b/avalanchego/snow/engine/snowman/block/test_height_indexed_vm.go index 83c0f77a..f2d8996d 100644 --- a/avalanchego/snow/engine/snowman/block/test_height_indexed_vm.go +++ b/avalanchego/snow/engine/snowman/block/test_height_indexed_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/test_state_summary.go b/avalanchego/snow/engine/snowman/block/test_state_summary.go index 56259e8c..139fb390 100644 --- a/avalanchego/snow/engine/snowman/block/test_state_summary.go +++ b/avalanchego/snow/engine/snowman/block/test_state_summary.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/test_state_syncable_vm.go b/avalanchego/snow/engine/snowman/block/test_state_syncable_vm.go index 3af4e7a5..bec3ebc8 100644 --- a/avalanchego/snow/engine/snowman/block/test_state_syncable_vm.go +++ b/avalanchego/snow/engine/snowman/block/test_state_syncable_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/test_vm.go b/avalanchego/snow/engine/snowman/block/test_vm.go index 5c96828e..6e0f3877 100644 --- a/avalanchego/snow/engine/snowman/block/test_vm.go +++ b/avalanchego/snow/engine/snowman/block/test_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/block/vm.go b/avalanchego/snow/engine/snowman/block/vm.go index c33221eb..063d9150 100644 --- a/avalanchego/snow/engine/snowman/block/vm.go +++ b/avalanchego/snow/engine/snowman/block/vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/snow/engine/snowman/bootstrap/block_job.go b/avalanchego/snow/engine/snowman/bootstrap/block_job.go index 58ce6f1f..6e557edb 100644 --- a/avalanchego/snow/engine/snowman/bootstrap/block_job.go +++ b/avalanchego/snow/engine/snowman/bootstrap/block_job.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go b/avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go index 6c5bdc38..a6617914 100644 --- a/avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go +++ b/avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/snowman/bootstrap/bootstrapper_test.go b/avalanchego/snow/engine/snowman/bootstrap/bootstrapper_test.go index 01c0ce0e..2a70c123 100644 --- a/avalanchego/snow/engine/snowman/bootstrap/bootstrapper_test.go +++ b/avalanchego/snow/engine/snowman/bootstrap/bootstrapper_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/snowman/bootstrap/config.go b/avalanchego/snow/engine/snowman/bootstrap/config.go index ffb393a2..c32b87e1 100644 --- a/avalanchego/snow/engine/snowman/bootstrap/config.go +++ b/avalanchego/snow/engine/snowman/bootstrap/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/snowman/bootstrap/metrics.go b/avalanchego/snow/engine/snowman/bootstrap/metrics.go index 3c5c1037..6de5d062 100644 --- a/avalanchego/snow/engine/snowman/bootstrap/metrics.go +++ b/avalanchego/snow/engine/snowman/bootstrap/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bootstrap diff --git a/avalanchego/snow/engine/snowman/config.go b/avalanchego/snow/engine/snowman/config.go index 709c8d05..53eca5d6 100644 --- a/avalanchego/snow/engine/snowman/config.go +++ b/avalanchego/snow/engine/snowman/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/config_test.go b/avalanchego/snow/engine/snowman/config_test.go index 409d7bbd..a7f1d40c 100644 --- a/avalanchego/snow/engine/snowman/config_test.go +++ b/avalanchego/snow/engine/snowman/config_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/engine.go b/avalanchego/snow/engine/snowman/engine.go index 22f3ac5c..0c34cb4c 100644 --- a/avalanchego/snow/engine/snowman/engine.go +++ b/avalanchego/snow/engine/snowman/engine.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/getter/getter.go b/avalanchego/snow/engine/snowman/getter/getter.go index fc6f0d94..c35ddae0 100644 --- a/avalanchego/snow/engine/snowman/getter/getter.go +++ b/avalanchego/snow/engine/snowman/getter/getter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package getter @@ -184,6 +184,6 @@ func (gh *getter) Get(nodeID ids.NodeID, requestID uint32, blkID ids.ID) error { } // Respond to the validator with the fetched block and the same requestID. - gh.sender.SendPut(nodeID, requestID, blkID, blk.Bytes()) + gh.sender.SendPut(nodeID, requestID, blk.Bytes()) return nil } diff --git a/avalanchego/snow/engine/snowman/getter/getter_test.go b/avalanchego/snow/engine/snowman/getter/getter_test.go index 6ac64887..dcf5d349 100644 --- a/avalanchego/snow/engine/snowman/getter/getter_test.go +++ b/avalanchego/snow/engine/snowman/getter/getter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package getter diff --git a/avalanchego/snow/engine/snowman/issuer.go b/avalanchego/snow/engine/snowman/issuer.go index 17917756..e0ea2204 100644 --- a/avalanchego/snow/engine/snowman/issuer.go +++ b/avalanchego/snow/engine/snowman/issuer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/memory_block.go b/avalanchego/snow/engine/snowman/memory_block.go index a71842b5..2ab28809 100644 --- a/avalanchego/snow/engine/snowman/memory_block.go +++ b/avalanchego/snow/engine/snowman/memory_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/metrics.go b/avalanchego/snow/engine/snowman/metrics.go index e18bac2b..279b05c3 100644 --- a/avalanchego/snow/engine/snowman/metrics.go +++ b/avalanchego/snow/engine/snowman/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/mocks/engine.go b/avalanchego/snow/engine/snowman/mocks/engine.go index 0f004ee2..d7f4916c 100644 --- a/avalanchego/snow/engine/snowman/mocks/engine.go +++ b/avalanchego/snow/engine/snowman/mocks/engine.go @@ -150,20 +150,6 @@ func (_m *Engine) Chits(validatorID ids.NodeID, requestID uint32, containerIDs [ return r0 } -// ChitsV2 provides a mock function with given fields: validatorID, requestID, containerIDs, containerID -func (_m *Engine) ChitsV2(validatorID ids.NodeID, requestID uint32, containerIDs []ids.ID, containerID ids.ID) error { - ret := _m.Called(validatorID, requestID, containerIDs, containerID) - - var r0 error - if rf, ok := ret.Get(0).(func(ids.NodeID, uint32, []ids.ID, ids.ID) error); ok { - r0 = rf(validatorID, requestID, containerIDs, containerID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // Connected provides a mock function with given fields: id, nodeVersion func (_m *Engine) Connected(id ids.NodeID, nodeVersion *version.Application) error { ret := _m.Called(id, nodeVersion) diff --git a/avalanchego/snow/engine/snowman/syncer/config.go b/avalanchego/snow/engine/snowman/syncer/config.go index 8c100898..0348b5e8 100644 --- a/avalanchego/snow/engine/snowman/syncer/config.go +++ b/avalanchego/snow/engine/snowman/syncer/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package syncer diff --git a/avalanchego/snow/engine/snowman/syncer/state_syncer.go b/avalanchego/snow/engine/snowman/syncer/state_syncer.go index 9a958c13..11e890ae 100644 --- a/avalanchego/snow/engine/snowman/syncer/state_syncer.go +++ b/avalanchego/snow/engine/snowman/syncer/state_syncer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package syncer diff --git a/avalanchego/snow/engine/snowman/syncer/state_syncer_test.go b/avalanchego/snow/engine/snowman/syncer/state_syncer_test.go index de55e319..32bc12b2 100644 --- a/avalanchego/snow/engine/snowman/syncer/state_syncer_test.go +++ b/avalanchego/snow/engine/snowman/syncer/state_syncer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package syncer diff --git a/avalanchego/snow/engine/snowman/syncer/utils_test.go b/avalanchego/snow/engine/snowman/syncer/utils_test.go index c24d1134..57923d1a 100644 --- a/avalanchego/snow/engine/snowman/syncer/utils_test.go +++ b/avalanchego/snow/engine/snowman/syncer/utils_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package syncer diff --git a/avalanchego/snow/engine/snowman/test_snowman_engine.go b/avalanchego/snow/engine/snowman/test_snowman_engine.go index 1948314e..d90e7ee7 100644 --- a/avalanchego/snow/engine/snowman/test_snowman_engine.go +++ b/avalanchego/snow/engine/snowman/test_snowman_engine.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/engine/snowman/transitive.go b/avalanchego/snow/engine/snowman/transitive.go index 1b9d7de1..27e1d77e 100644 --- a/avalanchego/snow/engine/snowman/transitive.go +++ b/avalanchego/snow/engine/snowman/transitive.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman @@ -9,6 +9,8 @@ import ( "go.uber.org/zap" + "github.com/ava-labs/avalanchego/cache" + "github.com/ava-labs/avalanchego/cache/metercacher" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/choices" @@ -20,6 +22,8 @@ import ( "github.com/ava-labs/avalanchego/version" ) +const nonVerifiedCacheSize = 128 + var _ Engine = &Transitive{} func New(config Config) (Engine, error) { @@ -54,6 +58,12 @@ type Transitive struct { // Block ID --> Parent ID nonVerifieds AncestorTree + // Block ID --> Block. + // A block is put into this cache if it was not able to be issued. A block + // fails to be issued if verification on the block or one of its ancestors + // occurs. + nonVerifiedCache cache.Cacher + // operations that are blocked on a block being issued. This could be // issuing another block, responding to a query, or applying votes to consensus blocked events.Blocker @@ -69,6 +79,14 @@ type Transitive struct { func newTransitive(config Config) (*Transitive, error) { config.Ctx.Log.Info("initializing consensus engine") + nonVerifiedCache, err := metercacher.New( + "non_verified_cache", + config.Ctx.Registerer, + &cache.LRU{Size: nonVerifiedCacheSize}, + ) + if err != nil { + return nil, err + } factory := poll.NewEarlyTermNoTraversalFactory(config.Params.Alpha) t := &Transitive{ Config: config, @@ -79,6 +97,7 @@ func newTransitive(config Config) (*Transitive, error) { AncestorsHandler: common.NewNoOpAncestorsHandler(config.Ctx.Log), pending: make(map[ids.ID]snowman.Block), nonVerifieds: NewAncestorTree(), + nonVerifiedCache: nonVerifiedCache, polls: poll.NewSet(factory, config.Ctx.Log, "", @@ -109,6 +128,22 @@ func (t *Transitive) Put(nodeID ids.NodeID, requestID uint32, blkBytes []byte) e return t.GetFailed(nodeID, requestID) } + actualBlkID := blk.ID() + expectedBlkID, ok := t.blkReqs.Get(nodeID, requestID) + // If the provided block is not the requested block, we need to explicitly + // mark the request as failed to avoid having a dangling dependency. + if ok && actualBlkID != expectedBlkID { + t.Ctx.Log.Debug("incorrect block returned in Put", + zap.Stringer("nodeID", nodeID), + zap.Uint32("requestID", requestID), + zap.Stringer("blkID", actualBlkID), + zap.Stringer("expectedBlkID", expectedBlkID), + ) + // We assume that [blk] is useless because it doesn't match what we + // expected. + return t.GetFailed(nodeID, requestID) + } + if t.wasIssued(blk) { t.metrics.numUselessPutBytes.Add(float64(len(blkBytes))) } @@ -138,13 +173,12 @@ func (t *Transitive) GetFailed(nodeID ids.NodeID, requestID uint32) error { // Because the get request was dropped, we no longer expect blkID to be issued. t.blocked.Abandon(blkID) + t.metrics.numRequests.Set(float64(t.blkReqs.Len())) t.metrics.numBlockers.Set(float64(t.blocked.Len())) return t.buildBlocks() } func (t *Transitive) PullQuery(nodeID ids.NodeID, requestID uint32, blkID ids.ID) error { - // TODO: once everyone supports ChitsV2 - we should be sending that message - // type here. t.Sender.SendChits(nodeID, requestID, []ids.ID{t.Consensus.Preference()}) // Try to issue [blkID] to consensus. @@ -157,8 +191,6 @@ func (t *Transitive) PullQuery(nodeID ids.NodeID, requestID uint32, blkID ids.ID } func (t *Transitive) PushQuery(nodeID ids.NodeID, requestID uint32, blkBytes []byte) error { - // TODO: once everyone supports ChitsV2 - we should be sending that message - // type here. t.Sender.SendChits(nodeID, requestID, []ids.ID{t.Consensus.Preference()}) blk, err := t.VM.ParseBlock(blkBytes) @@ -237,10 +269,6 @@ func (t *Transitive) Chits(nodeID ids.NodeID, requestID uint32, votes []ids.ID) return t.buildBlocks() } -func (t *Transitive) ChitsV2(vdr ids.NodeID, requestID uint32, _ []ids.ID, vote ids.ID) error { - return t.Chits(vdr, requestID, []ids.ID{vote}) -} - func (t *Transitive) QueryFailed(vdr ids.NodeID, requestID uint32) error { t.blocked.Register(&voter{ t: t, @@ -298,7 +326,7 @@ func (t *Transitive) Gossip() error { t.Ctx.Log.Verbo("gossiping accepted block to the network", zap.Stringer("blkID", blkID), ) - t.Sender.SendGossip(blkID, blk.Bytes()) + t.Sender.SendGossip(blk.Bytes()) return nil } @@ -312,7 +340,7 @@ func (t *Transitive) Shutdown() error { func (t *Transitive) Notify(msg common.Message) error { if msg != common.PendingTxs { t.Ctx.Log.Warn("received an unexpected message from the VM", - zap.Stringer("message", msg), + zap.Stringer("messageString", msg), ) return nil } @@ -407,6 +435,9 @@ func (t *Transitive) GetBlock(blkID ids.ID) (snowman.Block, error) { if blk, ok := t.pending[blkID]; ok { return blk, nil } + if blk, ok := t.nonVerifiedCache.Get(blkID); ok { + return blk.(snowman.Block), nil + } return t.VM.GetBlock(blkID) } @@ -721,33 +752,16 @@ func (t *Transitive) deliver(blk snowman.Block) error { // By ensuring that the parent is either processing or accepted, it is // guaranteed that the parent was successfully verified. This means that // calling Verify on this block is allowed. - - // make sure this block is valid - if err := blk.Verify(); err != nil { - t.Ctx.Log.Debug("block verification failed", - zap.Error(err), - ) - - // if verify fails, then all descendants are also invalid - t.addToNonVerifieds(blk) + blkAdded, err := t.addUnverifiedBlockToConsensus(blk) + if err != nil { + return err + } + if !blkAdded { t.blocked.Abandon(blkID) t.metrics.numBlocked.Set(float64(len(t.pending))) // Tracks performance statistics t.metrics.numBlockers.Set(float64(t.blocked.Len())) return t.errs.Err } - t.nonVerifieds.Remove(blkID) - t.metrics.numNonVerifieds.Set(float64(t.nonVerifieds.Len())) - t.Ctx.Log.Verbo("adding block to consensus", - zap.Stringer("blkID", blkID), - ) - wrappedBlk := &memoryBlock{ - Block: blk, - metrics: &t.metrics, - tree: t.nonVerifieds, - } - if err := t.Consensus.Add(wrappedBlk); err != nil { - return err - } // Add all the oracle blocks if they exist. We call verify on all the blocks // and add them to consensus before marking anything as fulfilled to avoid @@ -762,27 +776,14 @@ func (t *Transitive) deliver(blk snowman.Block) error { } for _, blk := range options { - if err := blk.Verify(); err != nil { - t.Ctx.Log.Debug("block verification failed", - zap.Error(err), - ) - dropped = append(dropped, blk) - // block fails verification, hold this in memory for bubbling - t.addToNonVerifieds(blk) - } else { - // correctly verified will be passed to consensus as processing block - // no need to keep it anymore - t.nonVerifieds.Remove(blk.ID()) - t.metrics.numNonVerifieds.Set(float64(t.nonVerifieds.Len())) - wrappedBlk := &memoryBlock{ - Block: blk, - metrics: &t.metrics, - tree: t.nonVerifieds, - } - if err := t.Consensus.Add(wrappedBlk); err != nil { - return err - } + blkAdded, err := t.addUnverifiedBlockToConsensus(blk) + if err != nil { + return err + } + if blkAdded { added = append(added, blk) + } else { + dropped = append(dropped, blk) } } } @@ -848,6 +849,35 @@ func (t *Transitive) addToNonVerifieds(blk snowman.Block) { // decided parents should not be in this map. if t.nonVerifieds.Has(parentID) || t.Consensus.Processing(parentID) { t.nonVerifieds.Add(blkID, parentID) + t.nonVerifiedCache.Put(blkID, blk) t.metrics.numNonVerifieds.Set(float64(t.nonVerifieds.Len())) } } + +// addUnverifiedBlockToConsensus returns whether the block was added and an +// error if one occurred while adding it to consensus. +func (t *Transitive) addUnverifiedBlockToConsensus(blk snowman.Block) (bool, error) { + // make sure this block is valid + if err := blk.Verify(); err != nil { + t.Ctx.Log.Debug("block verification failed", + zap.Error(err), + ) + + // if verify fails, then all descendants are also invalid + t.addToNonVerifieds(blk) + return false, nil + } + + blkID := blk.ID() + t.nonVerifieds.Remove(blkID) + t.nonVerifiedCache.Evict(blkID) + t.metrics.numNonVerifieds.Set(float64(t.nonVerifieds.Len())) + t.Ctx.Log.Verbo("adding block to consensus", + zap.Stringer("blkID", blkID), + ) + return true, t.Consensus.Add(&memoryBlock{ + Block: blk, + metrics: &t.metrics, + tree: t.nonVerifieds, + }) +} diff --git a/avalanchego/snow/engine/snowman/transitive_test.go b/avalanchego/snow/engine/snowman/transitive_test.go index cfa754a7..57b22374 100644 --- a/avalanchego/snow/engine/snowman/transitive_test.go +++ b/avalanchego/snow/engine/snowman/transitive_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman @@ -260,7 +260,7 @@ func TestEngineQuery(t *testing.T) { queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -271,7 +271,7 @@ func TestEngineQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk.ID() != blkID { + if !bytes.Equal(blk.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -331,7 +331,7 @@ func TestEngineQuery(t *testing.T) { } *queried = false - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -342,7 +342,7 @@ func TestEngineQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blkID != blk1.ID() { + if !bytes.Equal(blk1.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -467,7 +467,7 @@ func TestEngineMultipleQuery(t *testing.T) { queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -478,7 +478,7 @@ func TestEngineMultipleQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk0.ID() != blkID { + if !bytes.Equal(blk0.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -559,7 +559,7 @@ func TestEngineMultipleQuery(t *testing.T) { *queried = false secondQueryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -570,7 +570,7 @@ func TestEngineMultipleQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk1.ID() != blkID { + if !bytes.Equal(blk1.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -694,15 +694,15 @@ func TestEngineFetchBlock(t *testing.T) { } added := new(bool) - sender.SendPutF = func(inVdr ids.NodeID, requestID uint32, blkID ids.ID, blk []byte) { + sender.SendPutF = func(inVdr ids.NodeID, requestID uint32, blk []byte) { if vdr != inVdr { t.Fatalf("Wrong validator") } if requestID != 123 { t.Fatalf("Wrong request id") } - if gBlk.ID() != blkID { - t.Fatalf("Wrong blockID") + if !bytes.Equal(gBlk.Bytes(), blk) { + t.Fatalf("Asking for wrong block") } *added = true } @@ -770,7 +770,7 @@ func TestEnginePushQuery(t *testing.T) { } queried := new(bool) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, _ uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, _ uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -780,7 +780,7 @@ func TestEnginePushQuery(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk.ID() != blkID { + if !bytes.Equal(blk.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -822,7 +822,7 @@ func TestEngineBuildBlock(t *testing.T) { } queried := new(bool) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, _ uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, _ uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -951,7 +951,7 @@ func TestVoteCanceling(t *testing.T) { queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -962,7 +962,7 @@ func TestVoteCanceling(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk.ID() != blkID { + if !bytes.Equal(blk.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -1319,14 +1319,14 @@ func TestEngineBlockingChitResponse(t *testing.T) { } queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { *queryRequestID = requestID vdrSet := ids.NodeIDSet{} vdrSet.Add(vdr) if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blkID != issuedBlk.ID() { + if !bytes.Equal(issuedBlk.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -1432,7 +1432,7 @@ func TestEngineUndeclaredDependencyDeadlock(t *testing.T) { invalidBlkID := invalidBlk.ID() reqID := new(uint32) - sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, _ ids.ID, _ []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, _ []byte) { *reqID = requestID } sender.SendPullQueryF = func(_ ids.NodeIDSet, requestID uint32, _ ids.ID) {} @@ -1480,11 +1480,8 @@ func TestEngineGossip(t *testing.T) { } called := new(bool) - sender.SendGossipF = func(blkID ids.ID, blkBytes []byte) { + sender.SendGossipF = func(blkBytes []byte) { *called = true - if blkID != gBlk.ID() { - t.Fatal(errUnknownBlock) - } if !bytes.Equal(blkBytes, gBlk.Bytes()) { t.Fatal(errUnknownBytes) } @@ -1792,10 +1789,10 @@ func TestEngineAggressivePolling(t *testing.T) { } numPushed := new(int) - sender.SendPushQueryF = func(_ ids.NodeIDSet, _ uint32, _ ids.ID, _ []byte) { *numPushed++ } + sender.SendPushQueryF = func(ids.NodeIDSet, uint32, []byte) { *numPushed++ } numPulled := new(int) - sender.SendPullQueryF = func(_ ids.NodeIDSet, _ uint32, _ ids.ID) { *numPulled++ } + sender.SendPullQueryF = func(ids.NodeIDSet, uint32, ids.ID) { *numPulled++ } if err := te.Put(vdr, 0, pendingBlk.Bytes()); err != nil { t.Fatal(err) @@ -1887,7 +1884,7 @@ func TestEngineDoubleChit(t *testing.T) { queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -1898,7 +1895,7 @@ func TestEngineDoubleChit(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk.ID() != blkID { + if !bytes.Equal(blk.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -2024,7 +2021,7 @@ func TestEngineBuildBlockLimit(t *testing.T) { queried bool reqID uint32 ) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, rID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, rID uint32, _ []byte) { reqID = rID if queried { t.Fatalf("Asked multiple times") @@ -2152,7 +2149,7 @@ func TestEngineReceiveNewRejectedBlock(t *testing.T) { asked bool reqID uint32 ) - sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, _ ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, blkBytes []byte) { asked = true reqID = rID } @@ -2256,7 +2253,7 @@ func TestEngineRejectionAmplification(t *testing.T) { queried bool reqID uint32 ) - sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, _ ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, _ []byte) { queried = true reqID = rID } @@ -2290,7 +2287,7 @@ func TestEngineRejectionAmplification(t *testing.T) { queried = false var asked bool - sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, _ ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(ids.NodeIDSet, uint32, []byte) { queried = true } sender.SendGetF = func(_ ids.NodeID, rID uint32, blkID ids.ID) { @@ -2388,7 +2385,7 @@ func TestEngineTransitiveRejectionAmplificationDueToRejectedParent(t *testing.T) queried bool reqID uint32 ) - sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, _ ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, _ []byte) { queried = true reqID = rID } @@ -2484,7 +2481,7 @@ func TestEngineTransitiveRejectionAmplificationDueToInvalidParent(t *testing.T) queried bool reqID uint32 ) - sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, _ ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(_ ids.NodeIDSet, rID uint32, blkBytes []byte) { queried = true reqID = rID } @@ -2575,8 +2572,8 @@ func TestEngineNonPreferredAmplification(t *testing.T) { } } - sender.SendPushQueryF = func(_ ids.NodeIDSet, _ uint32, blkID ids.ID, _ []byte) { - if blkID == nonPreferredBlk.ID() { + sender.SendPushQueryF = func(_ ids.NodeIDSet, _ uint32, blkBytes []byte) { + if bytes.Equal(nonPreferredBlk.Bytes(), blkBytes) { t.Fatalf("gossiped non-preferred block") } } @@ -2684,7 +2681,7 @@ func TestEngineBubbleVotesThroughInvalidBlock(t *testing.T) { // [blk2] since it currently fails verification. queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -2695,7 +2692,7 @@ func TestEngineBubbleVotesThroughInvalidBlock(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk1.ID() != blkID { + if !bytes.Equal(blk1.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -2781,7 +2778,7 @@ func TestEngineBubbleVotesThroughInvalidBlock(t *testing.T) { } *queried = false // Prepare to PushQuery [blk2] after receiving a Gossip message with [blk2]. - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -2792,7 +2789,7 @@ func TestEngineBubbleVotesThroughInvalidBlock(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk2.ID() != blkID { + if !bytes.Equal(blk2.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -2921,7 +2918,7 @@ func TestEngineBubbleVotesThroughInvalidChain(t *testing.T) { // We should not PushQuery [blk3] because [blk2] wasn't issued. queried := new(bool) queryRequestID := new(uint32) - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { if *queried { t.Fatalf("Asked multiple times") } @@ -2932,7 +2929,7 @@ func TestEngineBubbleVotesThroughInvalidChain(t *testing.T) { if !inVdrs.Equals(vdrSet) { t.Fatalf("Asking wrong validator for preference") } - if blk1.ID() != blkID { + if !bytes.Equal(blk1.Bytes(), blkBytes) { t.Fatalf("Asking for wrong block") } } @@ -3108,12 +3105,10 @@ func TestSendMixedQuery(t *testing.T) { pushQuerySent := new(bool) pushQueryReqID := new(uint32) pushQueriedVdrs := ids.NodeIDSet{} - sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkID ids.ID, blkBytes []byte) { + sender.SendPushQueryF = func(inVdrs ids.NodeIDSet, requestID uint32, blkBytes []byte) { switch { case *pushQuerySent: t.Fatal("Asked multiple times") - case blkID != blk1.ID(): - t.Fatal("got unexpected block bytes instead of blk1") case !bytes.Equal(blkBytes, blk1.Bytes()): t.Fatal("got unexpected block bytes instead of blk1") } @@ -3152,3 +3147,150 @@ func TestSendMixedQuery(t *testing.T) { }) } } + +func TestEngineBuildBlockWithCachedNonVerifiedParent(t *testing.T) { + require := require.New(t) + vdr, _, sender, vm, te, gBlk := setupDefaultConfig(t) + + sender.Default(true) + + grandParentBlk := &snowman.TestBlock{ + TestDecidable: choices.TestDecidable{ + IDV: ids.GenerateTestID(), + StatusV: choices.Processing, + }, + ParentV: gBlk.ID(), + HeightV: 1, + BytesV: []byte{1}, + } + + parentBlkA := &snowman.TestBlock{ + TestDecidable: choices.TestDecidable{ + IDV: ids.GenerateTestID(), + StatusV: choices.Processing, + }, + ParentV: grandParentBlk.ID(), + HeightV: 2, + VerifyV: errors.New(""), // Reports as invalid + BytesV: []byte{2}, + } + + // Note that [parentBlkB] has the same [ID()] as [parentBlkA]; + // it's a different instantiation of the same block. + parentBlkB := &snowman.TestBlock{ + TestDecidable: choices.TestDecidable{ + IDV: parentBlkA.IDV, + StatusV: choices.Processing, + }, + ParentV: parentBlkA.ParentV, + HeightV: parentBlkA.HeightV, + BytesV: parentBlkA.BytesV, + } + + // Child of [parentBlkA]/[parentBlkB] + childBlk := &snowman.TestBlock{ + TestDecidable: choices.TestDecidable{ + IDV: ids.GenerateTestID(), + StatusV: choices.Processing, + }, + ParentV: parentBlkA.ID(), + HeightV: 3, + BytesV: []byte{3}, + } + + vm.ParseBlockF = func(b []byte) (snowman.Block, error) { + require.Equal(grandParentBlk.BytesV, b) + return grandParentBlk, nil + } + + vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) { + switch blkID { + case gBlk.ID(): + return gBlk, nil + case grandParentBlk.IDV: + return grandParentBlk, nil + default: + return nil, errUnknownBlock + } + } + + queryRequestGPID := new(uint32) + sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, blkBytes []byte) { + require.Equal(grandParentBlk.Bytes(), blkBytes) + *queryRequestGPID = requestID + } + + // Give the engine the grandparent + err := te.Put(vdr, 0, grandParentBlk.BytesV) + require.NoError(err) + + vm.ParseBlockF = func(b []byte) (snowman.Block, error) { + require.Equal(parentBlkA.BytesV, b) + return parentBlkA, nil + } + + // Give the node [parentBlkA]/[parentBlkB]. + // When it's parsed we get [parentBlkA] (not [parentBlkB]). + // [parentBlkA] fails verification and gets put into [te.nonVerifiedCache]. + err = te.Put(vdr, 0, parentBlkA.BytesV) + require.NoError(err) + + vm.ParseBlockF = func(b []byte) (snowman.Block, error) { + require.Equal(parentBlkB.BytesV, b) + return parentBlkB, nil + } + + vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) { + switch blkID { + case gBlk.ID(): + return gBlk, nil + case grandParentBlk.IDV: + return grandParentBlk, nil + case parentBlkB.IDV: + return parentBlkB, nil + default: + return nil, errUnknownBlock + } + } + + queryRequestAID := new(uint32) + sender.SendPushQueryF = func(_ ids.NodeIDSet, requestID uint32, blkBytes []byte) { + require.Equal(parentBlkA.Bytes(), blkBytes) + *queryRequestAID = requestID + } + sender.CantSendPullQuery = false + + // Give the engine [parentBlkA]/[parentBlkB] again. + // This time when we parse it we get [parentBlkB] (not [parentBlkA]). + // When we fetch it using [GetBlockF] we get [parentBlkB]. + // Note that [parentBlkB] doesn't fail verification and is issued into consensus. + // This evicts [parentBlkA] from [te.nonVerifiedCache]. + err = te.Put(vdr, 0, parentBlkA.BytesV) + require.NoError(err) + + // Give 2 chits for [parentBlkA]/[parentBlkB] + err = te.Chits(vdr, *queryRequestAID, []ids.ID{parentBlkB.IDV}) + require.NoError(err) + + err = te.Chits(vdr, *queryRequestGPID, []ids.ID{parentBlkB.IDV}) + require.NoError(err) + + // Assert that the blocks' statuses are correct. + // The evicted [parentBlkA] shouldn't be changed. + require.Equal(choices.Processing, parentBlkA.Status()) + require.Equal(choices.Accepted, parentBlkB.Status()) + + vm.BuildBlockF = func() (snowman.Block, error) { + return childBlk, nil + } + + sentQuery := new(bool) + sender.SendPushQueryF = func(ids.NodeIDSet, uint32, []byte) { + *sentQuery = true + } + + // Should issue a new block and send a query for it. + err = te.Notify(common.PendingTxs) + require.NoError(err) + require.True(*sentQuery) +} diff --git a/avalanchego/snow/engine/snowman/voter.go b/avalanchego/snow/engine/snowman/voter.go index f2886bab..33e35dfe 100644 --- a/avalanchego/snow/engine/snowman/voter.go +++ b/avalanchego/snow/engine/snowman/voter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman diff --git a/avalanchego/snow/events/blockable.go b/avalanchego/snow/events/blockable.go index a4e53da9..1698c6d3 100644 --- a/avalanchego/snow/events/blockable.go +++ b/avalanchego/snow/events/blockable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package events diff --git a/avalanchego/snow/events/blocker.go b/avalanchego/snow/events/blocker.go index 2d409ab5..7e3ad2ca 100644 --- a/avalanchego/snow/events/blocker.go +++ b/avalanchego/snow/events/blocker.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package events diff --git a/avalanchego/snow/events/blocker_test.go b/avalanchego/snow/events/blocker_test.go index 8971c3d6..cad6f03a 100644 --- a/avalanchego/snow/events/blocker_test.go +++ b/avalanchego/snow/events/blocker_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package events diff --git a/avalanchego/snow/networking/benchlist/benchable.go b/avalanchego/snow/networking/benchlist/benchable.go index 0fd4eb7b..843f1f76 100644 --- a/avalanchego/snow/networking/benchlist/benchable.go +++ b/avalanchego/snow/networking/benchlist/benchable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package benchlist diff --git a/avalanchego/snow/networking/benchlist/benchlist.go b/avalanchego/snow/networking/benchlist/benchlist.go index 94980dab..6e5197e6 100644 --- a/avalanchego/snow/networking/benchlist/benchlist.go +++ b/avalanchego/snow/networking/benchlist/benchlist.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package benchlist diff --git a/avalanchego/snow/networking/benchlist/benchlist_test.go b/avalanchego/snow/networking/benchlist/benchlist_test.go index 3c7beae1..60e82b44 100644 --- a/avalanchego/snow/networking/benchlist/benchlist_test.go +++ b/avalanchego/snow/networking/benchlist/benchlist_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package benchlist diff --git a/avalanchego/snow/networking/benchlist/manager.go b/avalanchego/snow/networking/benchlist/manager.go index 2c29e4cf..614c7794 100644 --- a/avalanchego/snow/networking/benchlist/manager.go +++ b/avalanchego/snow/networking/benchlist/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package benchlist diff --git a/avalanchego/snow/networking/benchlist/metrics.go b/avalanchego/snow/networking/benchlist/metrics.go index fa5c4141..7b132a08 100644 --- a/avalanchego/snow/networking/benchlist/metrics.go +++ b/avalanchego/snow/networking/benchlist/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package benchlist diff --git a/avalanchego/snow/networking/benchlist/test_benchable.go b/avalanchego/snow/networking/benchlist/test_benchable.go index 022fe945..1e525dd8 100644 --- a/avalanchego/snow/networking/benchlist/test_benchable.go +++ b/avalanchego/snow/networking/benchlist/test_benchable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package benchlist diff --git a/avalanchego/snow/networking/handler/handler.go b/avalanchego/snow/networking/handler/handler.go index eecdf73f..9b75f040 100644 --- a/avalanchego/snow/networking/handler/handler.go +++ b/avalanchego/snow/networking/handler/handler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package handler @@ -20,6 +20,7 @@ import ( "github.com/ava-labs/avalanchego/snow/networking/tracker" "github.com/ava-labs/avalanchego/snow/networking/worker" "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/version" ) @@ -62,7 +63,7 @@ type handler struct { clock mockable.Clock ctx *snow.ConsensusContext - mc message.Creator + mc message.InternalMsgBuilder // The validator set that validates this chain validators validators.Set // Receives messages from the VM @@ -100,7 +101,7 @@ type handler struct { // Initialize this consensus handler // [engine] must be initialized before initializing this handler func New( - mc message.Creator, + mc message.InternalMsgBuilder, ctx *snow.ConsensusContext, validators validators.Set, msgFromVMChan <-chan common.Message, @@ -366,9 +367,10 @@ func (h *handler) dispatchChans() { } } +// Any returned error is treated as fatal func (h *handler) handleSyncMsg(msg message.InboundMessage) error { h.ctx.Log.Debug("forwarding sync message to consensus", - zap.Stringer("message", msg), + zap.Stringer("messageString", msg), ) var ( @@ -398,183 +400,427 @@ func (h *handler) handleSyncMsg(msg message.InboundMessage) error { return err } + // Invariant: msg.Get(message.RequestID) must never error. The [ChainRouter] + // should have already successfully called this function. + // Invariant: Response messages can never be dropped here. This is because + // the timeout has already been cleared. This means the engine + // should be invoked with a failure message if parsing of the + // response fails. switch op { case message.GetStateSummaryFrontier: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetStateSummaryFrontier(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetStateSummaryFrontier(nodeID, requestID) case message.StateSummaryFrontier: - reqID := msg.Get(message.RequestID).(uint32) - summary := msg.Get(message.SummaryBytes).([]byte) - return engine.StateSummaryFrontier(nodeID, reqID, summary) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + summaryIntf, err := msg.Get(message.SummaryBytes) + if err != nil { + h.ctx.Log.Debug("message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.SummaryBytes), + zap.Error(err), + ) + return engine.GetStateSummaryFrontierFailed(nodeID, requestID) + } + summary := summaryIntf.([]byte) + + return engine.StateSummaryFrontier(nodeID, requestID, summary) case message.GetStateSummaryFrontierFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetStateSummaryFrontierFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetStateSummaryFrontierFailed(nodeID, requestID) case message.GetAcceptedStateSummary: - reqID := msg.Get(message.RequestID).(uint32) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + summaryHeights, err := getSummaryHeights(msg) if err != nil { - h.ctx.Log.Debug("malformed message", - zap.Stringer("messageOp", op), + h.ctx.Log.Debug("dropping message with invalid field", zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", reqID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.SummaryHeights), zap.Error(err), ) return nil } - return engine.GetAcceptedStateSummary(nodeID, reqID, summaryHeights) + + return engine.GetAcceptedStateSummary(nodeID, requestID, summaryHeights) case message.AcceptedStateSummary: - reqID := msg.Get(message.RequestID).(uint32) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + summaryIDs, err := getIDs(message.SummaryIDs, msg) if err != nil { - h.ctx.Log.Debug("malformed message", - zap.Stringer("messageOp", op), + h.ctx.Log.Debug("message with invalid field", zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", reqID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.SummaryIDs), zap.Error(err), ) - return engine.GetAcceptedStateSummaryFailed(nodeID, reqID) + return engine.GetAcceptedStateSummaryFailed(nodeID, requestID) } - return engine.AcceptedStateSummary(nodeID, reqID, summaryIDs) + + return engine.AcceptedStateSummary(nodeID, requestID, summaryIDs) case message.GetAcceptedStateSummaryFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetAcceptedStateSummaryFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetAcceptedStateSummaryFailed(nodeID, requestID) case message.GetAcceptedFrontier: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetAcceptedFrontier(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetAcceptedFrontier(nodeID, requestID) case message.AcceptedFrontier: - reqID := msg.Get(message.RequestID).(uint32) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + containerIDs, err := getIDs(message.ContainerIDs, msg) if err != nil { - h.ctx.Log.Debug("malformed message", - zap.Stringer("messageOp", op), + h.ctx.Log.Debug("message with invalid field", zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", reqID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerIDs), zap.Error(err), ) - return engine.GetAcceptedFrontierFailed(nodeID, reqID) + return engine.GetAcceptedFrontierFailed(nodeID, requestID) } - return engine.AcceptedFrontier(nodeID, reqID, containerIDs) + + return engine.AcceptedFrontier(nodeID, requestID, containerIDs) case message.GetAcceptedFrontierFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetAcceptedFrontierFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetAcceptedFrontierFailed(nodeID, requestID) case message.GetAccepted: - reqID := msg.Get(message.RequestID).(uint32) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + containerIDs, err := getIDs(message.ContainerIDs, msg) if err != nil { - h.ctx.Log.Debug("malformed message", - zap.Stringer("messageOp", op), + h.ctx.Log.Debug("dropping message with invalid field", zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", reqID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerIDs), zap.Error(err), ) return nil } - return engine.GetAccepted(nodeID, reqID, containerIDs) + + return engine.GetAccepted(nodeID, requestID, containerIDs) case message.Accepted: - reqID := msg.Get(message.RequestID).(uint32) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + containerIDs, err := getIDs(message.ContainerIDs, msg) if err != nil { - h.ctx.Log.Debug("malformed message", - zap.Stringer("messageOp", op), + h.ctx.Log.Debug("message with invalid field", zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", reqID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerIDs), zap.Error(err), ) - return engine.GetAcceptedFailed(nodeID, reqID) + return engine.GetAcceptedFailed(nodeID, requestID) } - return engine.Accepted(nodeID, reqID, containerIDs) + + return engine.Accepted(nodeID, requestID, containerIDs) case message.GetAcceptedFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetAcceptedFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetAcceptedFailed(nodeID, requestID) case message.GetAncestors: - reqID := msg.Get(message.RequestID).(uint32) - containerID, err := ids.ToID(msg.Get(message.ContainerID).([]byte)) - h.ctx.Log.AssertNoError(err) - return engine.GetAncestors(nodeID, reqID, containerID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + containerIDIntf, err := msg.Get(message.ContainerID) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerID), + zap.Error(err), + ) + return nil + } + containerIDBytes := containerIDIntf.([]byte) + containerID, err := ids.ToID(containerIDBytes) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerID), + zap.Error(err), + ) + return nil + } + + return engine.GetAncestors(nodeID, requestID, containerID) case message.GetAncestorsFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetAncestorsFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetAncestorsFailed(nodeID, requestID) case message.Ancestors: - reqID := msg.Get(message.RequestID).(uint32) - containers := msg.Get(message.MultiContainerBytes).([][]byte) - return engine.Ancestors(nodeID, reqID, containers) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + containersIntf, err := msg.Get(message.MultiContainerBytes) + if err != nil { + h.ctx.Log.Debug("message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.MultiContainerBytes), + zap.Error(err), + ) + return engine.GetAncestorsFailed(nodeID, requestID) + } + containers := containersIntf.([][]byte) + + return engine.Ancestors(nodeID, requestID, containers) case message.Get: - reqID := msg.Get(message.RequestID).(uint32) - containerID, err := ids.ToID(msg.Get(message.ContainerID).([]byte)) - h.ctx.Log.AssertNoError(err) - return engine.Get(nodeID, reqID, containerID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + containerIDIntf, err := msg.Get(message.ContainerID) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerID), + zap.Error(err), + ) + return nil + } + containerIDBytes := containerIDIntf.([]byte) + containerID, err := ids.ToID(containerIDBytes) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerID), + zap.Error(err), + ) + return nil + } + + return engine.Get(nodeID, requestID, containerID) case message.GetFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.GetFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.GetFailed(nodeID, requestID) case message.Put: - reqID := msg.Get(message.RequestID).(uint32) - container := msg.Get(message.ContainerBytes).([]byte) - return engine.Put(nodeID, reqID, container) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + containerIntf, err := msg.Get(message.ContainerBytes) + if err != nil { + // TODO: [requestID] can overflow, which means a timeout on the + // request before the overflow may not be handled properly. + if requestID == constants.GossipMsgRequestID { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerBytes), + zap.Error(err), + ) + return nil + } + + h.ctx.Log.Debug("message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerBytes), + zap.Error(err), + ) + return engine.GetFailed(nodeID, requestID) + } + container := containerIntf.([]byte) + + return engine.Put(nodeID, requestID, container) case message.PushQuery: - reqID := msg.Get(message.RequestID).(uint32) - container := msg.Get(message.ContainerBytes).([]byte) - return engine.PushQuery(nodeID, reqID, container) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + containerIntf, err := msg.Get(message.ContainerBytes) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerBytes), + zap.Error(err), + ) + return nil + } + container := containerIntf.([]byte) + + return engine.PushQuery(nodeID, requestID, container) case message.PullQuery: - reqID := msg.Get(message.RequestID).(uint32) - containerID, err := ids.ToID(msg.Get(message.ContainerID).([]byte)) - h.ctx.Log.AssertNoError(err) - return engine.PullQuery(nodeID, reqID, containerID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) - case message.Chits: - reqID := msg.Get(message.RequestID).(uint32) - votes, err := getIDs(message.ContainerIDs, msg) + containerIDIntf, err := msg.Get(message.ContainerID) if err != nil { - h.ctx.Log.Debug("malformed message", + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerID), + zap.Error(err), + ) + return nil + } + containerIDBytes := containerIDIntf.([]byte) + containerID, err := ids.ToID(containerIDBytes) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", reqID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerID), zap.Error(err), ) - return engine.QueryFailed(nodeID, reqID) + return nil + } + + return engine.PullQuery(nodeID, requestID, containerID) + + case message.Chits: + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err } - return engine.Chits(nodeID, reqID, votes) + requestID := requestIDIntf.(uint32) - case message.ChitsV2: - reqID := msg.Get(message.RequestID).(uint32) votes, err := getIDs(message.ContainerIDs, msg) if err != nil { - h.ctx.Log.Debug("malformed message", - zap.Stringer("messageOp", op), + h.ctx.Log.Debug("message with invalid field", zap.Stringer("nodeID", nodeID), - zap.Uint32("requestID", reqID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.ContainerIDs), zap.Error(err), ) - return engine.QueryFailed(nodeID, reqID) + return engine.QueryFailed(nodeID, requestID) } - vote, err := ids.ToID(msg.Get(message.ContainerID).([]byte)) - h.ctx.Log.AssertNoError(err) - return engine.ChitsV2(nodeID, reqID, votes, vote) + + return engine.Chits(nodeID, requestID, votes) case message.QueryFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.QueryFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.QueryFailed(nodeID, requestID) case message.Connected: - peerVersion := msg.Get(message.VersionStruct).(*version.Application) + peerVersionIntf, err := msg.Get(message.VersionStruct) + if err != nil { + return err + } + peerVersion := peerVersionIntf.(*version.Application) + return engine.Connected(nodeID, peerVersion) case message.Disconnected: @@ -600,9 +846,10 @@ func (h *handler) handleAsyncMsg(msg message.InboundMessage) { }) } +// Any returned error is treated as fatal func (h *handler) executeAsyncMsg(msg message.InboundMessage) error { h.ctx.Log.Debug("forwarding async message to consensus", - zap.Stringer("message", msg), + zap.Stringer("messageString", msg), ) var ( @@ -631,21 +878,71 @@ func (h *handler) executeAsyncMsg(msg message.InboundMessage) error { switch op { case message.AppRequest: - reqID := msg.Get(message.RequestID).(uint32) - appBytes := msg.Get(message.AppBytes).([]byte) - return engine.AppRequest(nodeID, reqID, msg.ExpirationTime(), appBytes) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + appBytesIntf, err := msg.Get(message.AppBytes) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.AppBytes), + zap.Error(err), + ) + return nil + } + appBytes := appBytesIntf.([]byte) + + return engine.AppRequest(nodeID, requestID, msg.ExpirationTime(), appBytes) case message.AppResponse: - reqID := msg.Get(message.RequestID).(uint32) - appBytes := msg.Get(message.AppBytes).([]byte) - return engine.AppResponse(nodeID, reqID, appBytes) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + appBytesIntf, err := msg.Get(message.AppBytes) + if err != nil { + h.ctx.Log.Debug("message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Uint32("requestID", requestID), + zap.Stringer("field", message.AppBytes), + zap.Error(err), + ) + return engine.AppRequestFailed(nodeID, requestID) + } + appBytes := appBytesIntf.([]byte) + + return engine.AppResponse(nodeID, requestID, appBytes) case message.AppRequestFailed: - reqID := msg.Get(message.RequestID).(uint32) - return engine.AppRequestFailed(nodeID, reqID) + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + return err + } + requestID := requestIDIntf.(uint32) + + return engine.AppRequestFailed(nodeID, requestID) case message.AppGossip: - appBytes := msg.Get(message.AppBytes).([]byte) + appBytesIntf, err := msg.Get(message.AppBytes) + if err != nil { + h.ctx.Log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Stringer("field", message.AppBytes), + zap.Error(err), + ) + return nil + } + appBytes := appBytesIntf.([]byte) + return engine.AppGossip(nodeID, appBytes) default: @@ -656,9 +953,10 @@ func (h *handler) executeAsyncMsg(msg message.InboundMessage) error { } } +// Any returned error is treated as fatal func (h *handler) handleChanMsg(msg message.InboundMessage) error { h.ctx.Log.Debug("forwarding chan message to consensus", - zap.Stringer("message", msg), + zap.Stringer("messageString", msg), ) var ( @@ -687,7 +985,12 @@ func (h *handler) handleChanMsg(msg message.InboundMessage) error { switch op := msg.Op(); op { case message.Notify: - vmMsg := msg.Get(message.VMMessage).(uint32) + vmMsgIntf, err := msg.Get(message.VMMessage) + if err != nil { + return err + } + vmMsg := vmMsgIntf.(uint32) + return engine.Notify(common.Message(vmMsg)) case message.GossipRequest: @@ -732,7 +1035,7 @@ func (h *handler) popUnexpiredMsg(queue MessageQueue, expired prometheus.Counter h.ctx.Log.Verbo("dropping message", zap.String("reason", "timeout"), zap.Stringer("nodeID", msg.NodeID()), - zap.Stringer("message", msg), + zap.Stringer("messageString", msg), ) expired.Inc() msg.OnFinishedHandling() diff --git a/avalanchego/snow/networking/handler/handler_test.go b/avalanchego/snow/networking/handler/handler_test.go index bfac359c..14754d68 100644 --- a/avalanchego/snow/networking/handler/handler_test.go +++ b/avalanchego/snow/networking/handler/handler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package handler @@ -26,7 +26,7 @@ func TestHandlerDropsTimedOutMessages(t *testing.T) { called := make(chan struct{}) metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) ctx := snow.DefaultConsensusContextTest() @@ -111,7 +111,7 @@ func TestHandlerClosesOnError(t *testing.T) { err := vdrs.AddWeight(ids.GenerateTestNodeID(), 1) require.NoError(t, err) metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) resourceTracker, err := tracker.NewResourceTracker(prometheus.NewRegistry(), resource.NoUsage, meter.ContinuousFactory{}, time.Second) @@ -181,10 +181,8 @@ func TestHandlerDropsGossipDuringBootstrapping(t *testing.T) { vdrs := validators.NewSet() err := vdrs.AddWeight(ids.GenerateTestNodeID(), 1) require.NoError(t, err) - metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) - require.NoError(t, err) + mc := message.NewInternalBuilder() resourceTracker, err := tracker.NewResourceTracker(prometheus.NewRegistry(), resource.NoUsage, meter.ContinuousFactory{}, time.Second) require.NoError(t, err) handlerIntf, err := New( @@ -244,10 +242,8 @@ func TestHandlerDispatchInternal(t *testing.T) { vdrs := validators.NewSet() err := vdrs.AddWeight(ids.GenerateTestNodeID(), 1) require.NoError(t, err) - metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) - require.NoError(t, err) + mc := message.NewInternalBuilder() resourceTracker, err := tracker.NewResourceTracker(prometheus.NewRegistry(), resource.NoUsage, meter.ContinuousFactory{}, time.Second) require.NoError(t, err) handler, err := New( diff --git a/avalanchego/snow/networking/handler/message_queue.go b/avalanchego/snow/networking/handler/message_queue.go index 478e99df..38f41688 100644 --- a/avalanchego/snow/networking/handler/message_queue.go +++ b/avalanchego/snow/networking/handler/message_queue.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package handler diff --git a/avalanchego/snow/networking/handler/message_queue_metrics.go b/avalanchego/snow/networking/handler/message_queue_metrics.go index dc509b09..245d0c7e 100644 --- a/avalanchego/snow/networking/handler/message_queue_metrics.go +++ b/avalanchego/snow/networking/handler/message_queue_metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package handler diff --git a/avalanchego/snow/networking/handler/message_queue_test.go b/avalanchego/snow/networking/handler/message_queue_test.go index 77201b21..e94c4fa3 100644 --- a/avalanchego/snow/networking/handler/message_queue_test.go +++ b/avalanchego/snow/networking/handler/message_queue_test.go @@ -1,9 +1,10 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package handler import ( + "fmt" "testing" "time" @@ -21,126 +22,137 @@ import ( ) func TestQueue(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - require := require.New(t) - cpuTracker := tracker.NewMockTracker(ctrl) - vdrs := validators.NewSet() - vdr1ID, vdr2ID := ids.GenerateTestNodeID(), ids.GenerateTestNodeID() - require.NoError(vdrs.AddWeight(vdr1ID, 1)) - require.NoError(vdrs.AddWeight(vdr2ID, 1)) - mIntf, err := NewMessageQueue(logging.NoLog{}, vdrs, cpuTracker, "", prometheus.NewRegistry(), message.SynchronousOps) - require.NoError(err) - u := mIntf.(*messageQueue) - currentTime := time.Now() - u.clock.Set(currentTime) - - mc, err := message.NewCreator(prometheus.NewRegistry(), true, "dummyNamespace", 10*time.Second) - require.NoError(err) - mc.SetTime(currentTime) - msg1 := mc.InboundPut(ids.Empty, - 0, - ids.GenerateTestID(), - nil, - vdr1ID, - ) - - // Push then pop should work regardless of usage when there are no other - // messages on [u.msgs] - cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.1).Times(1) - u.Push(msg1) - require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) - require.EqualValues(1, u.Len()) - gotMsg1, ok := u.Pop() - require.True(ok) - require.Len(u.nodeToUnprocessedMsgs, 0) - require.EqualValues(0, u.Len()) - require.EqualValues(msg1, gotMsg1) - - cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.0).Times(1) - u.Push(msg1) - require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) - require.EqualValues(1, u.Len()) - gotMsg1, ok = u.Pop() - require.True(ok) - require.Len(u.nodeToUnprocessedMsgs, 0) - require.EqualValues(0, u.Len()) - require.EqualValues(msg1, gotMsg1) - - cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(1.0).Times(1) - u.Push(msg1) - require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) - require.EqualValues(1, u.Len()) - gotMsg1, ok = u.Pop() - require.True(ok) - require.Len(u.nodeToUnprocessedMsgs, 0) - require.EqualValues(0, u.Len()) - require.EqualValues(msg1, gotMsg1) - - cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.0).Times(1) - u.Push(msg1) - require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) - require.EqualValues(1, u.Len()) - gotMsg1, ok = u.Pop() - require.True(ok) - require.Len(u.nodeToUnprocessedMsgs, 0) - require.EqualValues(0, u.Len()) - require.EqualValues(msg1, gotMsg1) - - // Push msg1 from vdr1ID - u.Push(msg1) - require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) - require.EqualValues(1, u.Len()) - - msg2 := mc.InboundGet(ids.Empty, 0, 0, ids.Empty, vdr2ID) - - // Push msg2 from vdr2ID - u.Push(msg2) - require.EqualValues(2, u.Len()) - require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr2ID]) - // Set vdr1's usage to 99% and vdr2's to .01 - cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(.99).Times(2) - cpuTracker.EXPECT().Usage(vdr2ID, gomock.Any()).Return(.01).Times(1) - // Pop should return msg2 first because vdr1 has exceeded it's portion of CPU time - gotMsg2, ok := u.Pop() - require.True(ok) - require.EqualValues(1, u.Len()) - require.EqualValues(msg2, gotMsg2) - gotMsg1, ok = u.Pop() - require.True(ok) - require.EqualValues(msg1, gotMsg1) - require.Len(u.nodeToUnprocessedMsgs, 0) - require.EqualValues(0, u.Len()) - - // u is now empty - // Non-validators should be able to put messages onto [u] - nonVdrNodeID1, nonVdrNodeID2 := ids.GenerateTestNodeID(), ids.GenerateTestNodeID() - msg3 := mc.InboundPullQuery(ids.Empty, 0, 0, ids.Empty, nonVdrNodeID1) - msg4 := mc.InboundPushQuery(ids.Empty, 0, 0, ids.Empty, nil, nonVdrNodeID2) - u.Push(msg3) - u.Push(msg4) - u.Push(msg1) - require.EqualValues(3, u.Len()) - - // msg1 should get popped first because nonVdrNodeID1 and nonVdrNodeID2 - // exceeded their limit - cpuTracker.EXPECT().Usage(nonVdrNodeID1, gomock.Any()).Return(.34).Times(1) - cpuTracker.EXPECT().Usage(nonVdrNodeID2, gomock.Any()).Return(.34).Times(2) - cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.0).Times(1) - - // u.msgs is [msg3, msg4, msg1] - gotMsg1, ok = u.Pop() - require.True(ok) - require.EqualValues(msg1, gotMsg1) - // u.msgs is [msg3, msg4] - cpuTracker.EXPECT().Usage(nonVdrNodeID1, gomock.Any()).Return(.51).Times(2) - gotMsg4, ok := u.Pop() - require.True(ok) - require.EqualValues(msg4, gotMsg4) - // u.msgs is [msg3] - gotMsg3, ok := u.Pop() - require.True(ok) - require.EqualValues(msg3, gotMsg3) - require.EqualValues(0, u.Len()) + for _, useProto := range []bool{false, true} { + t.Run(fmt.Sprintf("use proto buf message creator %v", useProto), func(tt *testing.T) { + ctrl := gomock.NewController(tt) + defer ctrl.Finish() + + require := require.New(tt) + cpuTracker := tracker.NewMockTracker(ctrl) + vdrs := validators.NewSet() + vdr1ID, vdr2ID := ids.GenerateTestNodeID(), ids.GenerateTestNodeID() + require.NoError(vdrs.AddWeight(vdr1ID, 1)) + require.NoError(vdrs.AddWeight(vdr2ID, 1)) + mIntf, err := NewMessageQueue(logging.NoLog{}, vdrs, cpuTracker, "", prometheus.NewRegistry(), message.SynchronousOps) + require.NoError(err) + u := mIntf.(*messageQueue) + currentTime := time.Now() + u.clock.Set(currentTime) + + metrics := prometheus.NewRegistry() + var mc message.Creator + if !useProto { + mc, err = message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) + } else { + mc, err = message.NewCreatorWithProto(metrics, "dummyNamespace", true, 10*time.Second) + } + require.NoError(err) + + mc.SetTime(currentTime) + msg1 := mc.InboundPut( + ids.Empty, + 0, + nil, + vdr1ID, + ) + + // Push then pop should work regardless of usage when there are no other + // messages on [u.msgs] + cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.1).Times(1) + u.Push(msg1) + require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) + require.EqualValues(1, u.Len()) + gotMsg1, ok := u.Pop() + require.True(ok) + require.Len(u.nodeToUnprocessedMsgs, 0) + require.EqualValues(0, u.Len()) + require.EqualValues(msg1, gotMsg1) + + cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.0).Times(1) + u.Push(msg1) + require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) + require.EqualValues(1, u.Len()) + gotMsg1, ok = u.Pop() + require.True(ok) + require.Len(u.nodeToUnprocessedMsgs, 0) + require.EqualValues(0, u.Len()) + require.EqualValues(msg1, gotMsg1) + + cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(1.0).Times(1) + u.Push(msg1) + require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) + require.EqualValues(1, u.Len()) + gotMsg1, ok = u.Pop() + require.True(ok) + require.Len(u.nodeToUnprocessedMsgs, 0) + require.EqualValues(0, u.Len()) + require.EqualValues(msg1, gotMsg1) + + cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.0).Times(1) + u.Push(msg1) + require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) + require.EqualValues(1, u.Len()) + gotMsg1, ok = u.Pop() + require.True(ok) + require.Len(u.nodeToUnprocessedMsgs, 0) + require.EqualValues(0, u.Len()) + require.EqualValues(msg1, gotMsg1) + + // Push msg1 from vdr1ID + u.Push(msg1) + require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr1ID]) + require.EqualValues(1, u.Len()) + + msg2 := mc.InboundGet(ids.Empty, 0, 0, ids.Empty, vdr2ID) + + // Push msg2 from vdr2ID + u.Push(msg2) + require.EqualValues(2, u.Len()) + require.EqualValues(1, u.nodeToUnprocessedMsgs[vdr2ID]) + // Set vdr1's usage to 99% and vdr2's to .01 + cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(.99).Times(2) + cpuTracker.EXPECT().Usage(vdr2ID, gomock.Any()).Return(.01).Times(1) + // Pop should return msg2 first because vdr1 has exceeded it's portion of CPU time + gotMsg2, ok := u.Pop() + require.True(ok) + require.EqualValues(1, u.Len()) + require.EqualValues(msg2, gotMsg2) + gotMsg1, ok = u.Pop() + require.True(ok) + require.EqualValues(msg1, gotMsg1) + require.Len(u.nodeToUnprocessedMsgs, 0) + require.EqualValues(0, u.Len()) + + // u is now empty + // Non-validators should be able to put messages onto [u] + nonVdrNodeID1, nonVdrNodeID2 := ids.GenerateTestNodeID(), ids.GenerateTestNodeID() + msg3 := mc.InboundPullQuery(ids.Empty, 0, 0, ids.Empty, nonVdrNodeID1) + msg4 := mc.InboundPushQuery(ids.Empty, 0, 0, nil, nonVdrNodeID2) + u.Push(msg3) + u.Push(msg4) + u.Push(msg1) + require.EqualValues(3, u.Len()) + + // msg1 should get popped first because nonVdrNodeID1 and nonVdrNodeID2 + // exceeded their limit + cpuTracker.EXPECT().Usage(nonVdrNodeID1, gomock.Any()).Return(.34).Times(1) + cpuTracker.EXPECT().Usage(nonVdrNodeID2, gomock.Any()).Return(.34).Times(2) + cpuTracker.EXPECT().Usage(vdr1ID, gomock.Any()).Return(0.0).Times(1) + + // u.msgs is [msg3, msg4, msg1] + gotMsg1, ok = u.Pop() + require.True(ok) + require.EqualValues(msg1, gotMsg1) + // u.msgs is [msg3, msg4] + cpuTracker.EXPECT().Usage(nonVdrNodeID1, gomock.Any()).Return(.51).Times(2) + gotMsg4, ok := u.Pop() + require.True(ok) + require.EqualValues(msg4, gotMsg4) + // u.msgs is [msg3] + gotMsg3, ok := u.Pop() + require.True(ok) + require.EqualValues(msg3, gotMsg3) + require.EqualValues(0, u.Len()) + }) + } } diff --git a/avalanchego/snow/networking/handler/metrics.go b/avalanchego/snow/networking/handler/metrics.go index 109c3fa6..853c0999 100644 --- a/avalanchego/snow/networking/handler/metrics.go +++ b/avalanchego/snow/networking/handler/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package handler diff --git a/avalanchego/snow/networking/handler/parser.go b/avalanchego/snow/networking/handler/parser.go index 349bcc7f..d55759f3 100644 --- a/avalanchego/snow/networking/handler/parser.go +++ b/avalanchego/snow/networking/handler/parser.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package handler @@ -16,10 +16,14 @@ var ( ) func getIDs(field message.Field, msg message.InboundMessage) ([]ids.ID, error) { - idsBytes := msg.Get(field).([][]byte) + idsBytesIntf, err := msg.Get(field) + if err != nil { + return nil, err + } + idsBytes := idsBytesIntf.([][]byte) + res := make([]ids.ID, len(idsBytes)) idSet := ids.NewSet(len(idsBytes)) - for i, bytes := range idsBytes { id, err := ids.ToID(bytes) if err != nil { @@ -35,9 +39,13 @@ func getIDs(field message.Field, msg message.InboundMessage) ([]ids.ID, error) { } func getSummaryHeights(msg message.InboundMessage) ([]uint64, error) { - heights := msg.Get(message.SummaryHeights).([]uint64) - heightsSet := make(map[uint64]struct{}, len(heights)) + heightsIntf, err := msg.Get(message.SummaryHeights) + if err != nil { + return nil, err + } + heights := heightsIntf.([]uint64) + heightsSet := make(map[uint64]struct{}, len(heights)) for _, height := range heights { if _, found := heightsSet[height]; found { return nil, errDuplicatedHeight diff --git a/avalanchego/snow/networking/router/chain_router.go b/avalanchego/snow/networking/router/chain_router.go index 83e28ba3..172e8261 100644 --- a/avalanchego/snow/networking/router/chain_router.go +++ b/avalanchego/snow/networking/router/chain_router.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package router @@ -55,7 +55,7 @@ type peer struct { type ChainRouter struct { clock mockable.Clock log logging.Logger - msgCreator message.Creator + msgCreator message.InternalMsgBuilder lock sync.Mutex chains map[ids.ID]handler.Handler @@ -75,7 +75,7 @@ type ChainRouter struct { // Parameters for doing health checks healthConfig HealthConfig // aggregator of requests based on their time - timedRequests linkedhashmap.LinkedHashmap + timedRequests linkedhashmap.LinkedHashmap[ids.ID, requestEntry] // Must only be accessed in method [createRequestID]. // [lock] must be held when [requestIDBytes] is accessed. requestIDBytes []byte @@ -89,7 +89,7 @@ type ChainRouter struct { func (cr *ChainRouter) Initialize( nodeID ids.NodeID, log logging.Logger, - msgCreator message.Creator, + msgCreator message.InternalMsgBuilder, timeoutManager timeout.Manager, closeTimeout time.Duration, criticalChains ids.Set, @@ -107,7 +107,7 @@ func (cr *ChainRouter) Initialize( cr.benched = make(map[ids.NodeID]ids.Set) cr.criticalChains = criticalChains cr.onFatal = onFatal - cr.timedRequests = linkedhashmap.New() + cr.timedRequests = linkedhashmap.New[ids.ID, requestEntry]() cr.peers = make(map[ids.NodeID]*peer) cr.healthConfig = healthConfig cr.requestIDBytes = make([]byte, hashing.AddrLen+hashing.HashLen+wrappers.IntLen+wrappers.ByteLen) // Validator ID, Chain ID, Request ID, Msg Type @@ -175,8 +175,32 @@ func (cr *ChainRouter) RegisterRequest( func (cr *ChainRouter) HandleInbound(msg message.InboundMessage) { nodeID := msg.NodeID() op := msg.Op() - chainID, err := ids.ToID(msg.Get(message.ChainID).([]byte)) - cr.log.AssertNoError(err) + + chainIDIntf, err := msg.Get(message.ChainID) + if err != nil { + cr.log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Stringer("field", message.ChainID), + zap.Error(err), + ) + + msg.OnFinishedHandling() + return + } + chainIDBytes := chainIDIntf.([]byte) + chainID, err := ids.ToID(chainIDBytes) + if err != nil { + cr.log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Stringer("field", message.ChainID), + zap.Error(err), + ) + + msg.OnFinishedHandling() + return + } // AppGossip is the only message currently not containing a requestID // Here we assign the requestID already in use for gossiped containers @@ -185,7 +209,21 @@ func (cr *ChainRouter) HandleInbound(msg message.InboundMessage) { if op == message.AppGossip { requestID = constants.GossipMsgRequestID } else { - requestID = msg.Get(message.RequestID).(uint32) + // Invariant: Getting a [RequestID] must never error in the handler. Any + // verification performed by the message is done here. + requestIDIntf, err := msg.Get(message.RequestID) + if err != nil { + cr.log.Debug("dropping message with invalid field", + zap.Stringer("nodeID", nodeID), + zap.Stringer("messageOp", op), + zap.Stringer("field", message.RequestID), + zap.Error(err), + ) + + msg.OnFinishedHandling() + return + } + requestID = requestIDIntf.(uint32) } cr.lock.Lock() @@ -207,6 +245,8 @@ func (cr *ChainRouter) HandleInbound(msg message.InboundMessage) { ctx := chain.Context() + // TODO: [requestID] can overflow, which means a timeout on the request + // before the overflow may not be handled properly. if _, notRequested := message.UnrequestedOps[op]; notRequested || (op == message.Put && requestID == constants.GossipMsgRequestID) { if ctx.IsExecuting() { @@ -441,7 +481,7 @@ func (cr *ChainRouter) HealthCheck() (interface{}, error) { now := cr.clock.Time() processingRequest := now if _, longestRunning, exists := cr.timedRequests.Oldest(); exists { - processingRequest = longestRunning.(requestEntry).time + processingRequest = longestRunning.time } timeReqRunning := now.Sub(processingRequest) isOutstanding := timeReqRunning <= cr.healthConfig.MaxOutstandingDuration @@ -502,25 +542,19 @@ func (cr *ChainRouter) clearRequest( // Create the request ID of the request we sent that this message is (allegedly) in response to. uniqueRequestID := cr.createRequestID(nodeID, chainID, requestID, op) // Mark that an outstanding request has been fulfilled - requestIntf, exists := cr.timedRequests.Get(uniqueRequestID) + request, exists := cr.timedRequests.Get(uniqueRequestID) if !exists { return uniqueRequestID, nil } cr.timedRequests.Delete(uniqueRequestID) cr.metrics.outstandingRequests.Set(float64(cr.timedRequests.Len())) - - request := requestIntf.(requestEntry) return uniqueRequestID, &request } // Assumes [cr.lock] is held. // Assumes [message.Op] is an alias of byte. func (cr *ChainRouter) createRequestID(nodeID ids.NodeID, chainID ids.ID, requestID uint32, op message.Op) ids.ID { - // Make sure to standardize chits messages. - if op == message.ChitsV2 { - op = message.Chits - } copy(cr.requestIDBytes, nodeID[:]) copy(cr.requestIDBytes[hashing.AddrLen:], chainID[:]) binary.BigEndian.PutUint32(cr.requestIDBytes[hashing.AddrLen+hashing.HashLen:], requestID) diff --git a/avalanchego/snow/networking/router/chain_router_metrics.go b/avalanchego/snow/networking/router/chain_router_metrics.go index 34683fc6..487771df 100644 --- a/avalanchego/snow/networking/router/chain_router_metrics.go +++ b/avalanchego/snow/networking/router/chain_router_metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package router diff --git a/avalanchego/snow/networking/router/chain_router_test.go b/avalanchego/snow/networking/router/chain_router_test.go index 61bad851..124aa190 100644 --- a/avalanchego/snow/networking/router/chain_router_test.go +++ b/avalanchego/snow/networking/router/chain_router_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package router @@ -45,16 +45,12 @@ func TestShutdown(t *testing.T) { "", prometheus.NewRegistry(), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) go tm.Dispatch() chainRouter := ChainRouter{} - metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) - require.NoError(t, err) + mc := message.NewInternalBuilder() err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, tm, time.Second, ids.Set{}, ids.Set{}, nil, HealthConfig{}, "", prometheus.NewRegistry()) require.NoError(t, err) @@ -141,14 +137,12 @@ func TestShutdownTimesOut(t *testing.T) { "", metrics, ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) go tm.Dispatch() chainRouter := ChainRouter{} - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) err = chainRouter.Initialize(ids.EmptyNodeID, @@ -250,17 +244,13 @@ func TestRouterTimeout(t *testing.T) { "", prometheus.NewRegistry(), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) go tm.Dispatch() // Create a router chainRouter := ChainRouter{} - metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) - require.NoError(t, err) + mc := message.NewInternalBuilder() err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, tm, time.Millisecond, ids.Set{}, ids.Set{}, nil, HealthConfig{}, "", prometheus.NewRegistry()) require.NoError(t, err) @@ -374,15 +364,14 @@ func TestRouterClearTimeouts(t *testing.T) { "", prometheus.NewRegistry(), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) go tm.Dispatch() // Create a router chainRouter := ChainRouter{} + metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, tm, time.Millisecond, ids.Set{}, ids.Set{}, nil, HealthConfig{}, "", prometheus.NewRegistry()) @@ -449,7 +438,7 @@ func TestRouterClearTimeouts(t *testing.T) { var inMsg message.InboundMessage // Put - inMsg = mc.InboundPut(ctx.ChainID, 0, ids.GenerateTestID(), nil, vID) + inMsg = mc.InboundPut(ctx.ChainID, 0, nil, vID) chainRouter.HandleInbound(inMsg) // Ancestors @@ -486,15 +475,14 @@ func TestValidatorOnlyMessageDrops(t *testing.T) { "", prometheus.NewRegistry(), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) go tm.Dispatch() // Create a router chainRouter := ChainRouter{} + metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, tm, time.Millisecond, ids.Set{}, ids.Set{}, nil, HealthConfig{}, "", prometheus.NewRegistry()) @@ -587,7 +575,7 @@ func TestValidatorOnlyMessageDrops(t *testing.T) { err = vdrs.Set(validators.NewSet().List()) require.NoError(t, err) - inMsg = mc.InboundPut(ctx.ChainID, reqID, ids.GenerateTestID(), nil, nID) + inMsg = mc.InboundPut(ctx.ChainID, reqID, nil, nID) chainRouter.HandleInbound(inMsg) // shouldn't clear out timed request, as the request should be cleared when diff --git a/avalanchego/snow/networking/router/health.go b/avalanchego/snow/networking/router/health.go index 11a66b08..8721de6a 100644 --- a/avalanchego/snow/networking/router/health.go +++ b/avalanchego/snow/networking/router/health.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package router diff --git a/avalanchego/snow/networking/router/inbound_handler.go b/avalanchego/snow/networking/router/inbound_handler.go index 90083314..5c5aa6f5 100644 --- a/avalanchego/snow/networking/router/inbound_handler.go +++ b/avalanchego/snow/networking/router/inbound_handler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package router diff --git a/avalanchego/snow/networking/router/router.go b/avalanchego/snow/networking/router/router.go index 5ee918d8..8eac6d5f 100644 --- a/avalanchego/snow/networking/router/router.go +++ b/avalanchego/snow/networking/router/router.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package router @@ -26,7 +26,7 @@ type Router interface { Initialize( nodeID ids.NodeID, log logging.Logger, - msgCreator message.Creator, + msgCreator message.InternalMsgBuilder, timeouts timeout.Manager, shutdownTimeout time.Duration, criticalChains ids.Set, diff --git a/avalanchego/snow/networking/sender/external_sender.go b/avalanchego/snow/networking/sender/external_sender.go index 10d63ac8..e772b97b 100644 --- a/avalanchego/snow/networking/sender/external_sender.go +++ b/avalanchego/snow/networking/sender/external_sender.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sender diff --git a/avalanchego/snow/networking/sender/sender.go b/avalanchego/snow/networking/sender/sender.go index b97f25c9..e58db4d7 100644 --- a/avalanchego/snow/networking/sender/sender.go +++ b/avalanchego/snow/networking/sender/sender.go @@ -1,10 +1,11 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sender import ( "fmt" + "time" "github.com/prometheus/client_golang/prometheus" @@ -17,6 +18,7 @@ import ( "github.com/ava-labs/avalanchego/snow/networking/router" "github.com/ava-labs/avalanchego/snow/networking/timeout" "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/timer/mockable" ) var _ common.Sender = &sender{} @@ -39,11 +41,18 @@ type GossipConfig struct { // sender registers outbound requests with [router] so that [router] // fires a timeout if we don't get a response to the request. type sender struct { - ctx *snow.ConsensusContext - msgCreator message.Creator - sender ExternalSender // Actually does the sending over the network - router router.Router - timeouts timeout.Manager + ctx *snow.ConsensusContext + msgCreator message.Creator + msgCreatorWithProto message.Creator + + // TODO: remove this once we complete banff migration + banffTime time.Time + + clock mockable.Clock + + sender ExternalSender // Actually does the sending over the network + router router.Router + timeouts timeout.Manager gossipConfig GossipConfig @@ -55,19 +64,23 @@ type sender struct { func New( ctx *snow.ConsensusContext, msgCreator message.Creator, + msgCreatorWithProto message.Creator, + banffTime time.Time, externalSender ExternalSender, router router.Router, timeouts timeout.Manager, gossipConfig GossipConfig, ) (common.Sender, error) { s := &sender{ - ctx: ctx, - msgCreator: msgCreator, - sender: externalSender, - router: router, - timeouts: timeouts, - gossipConfig: gossipConfig, - failedDueToBench: make(map[message.Op]prometheus.Counter, len(message.ConsensusRequestOps)), + ctx: ctx, + msgCreator: msgCreator, + msgCreatorWithProto: msgCreatorWithProto, + banffTime: banffTime, + sender: externalSender, + router: router, + timeouts: timeouts, + gossipConfig: gossipConfig, + failedDueToBench: make(map[message.Op]prometheus.Counter, len(message.ConsensusRequestOps)), } for _, op := range message.ConsensusRequestOps { @@ -85,6 +98,14 @@ func New( return s, nil } +func (s *sender) getMsgCreator() message.Creator { + now := s.clock.Time() + if now.Before(s.banffTime) { + return s.msgCreator + } + return s.msgCreatorWithProto +} + func (s *sender) SendGetStateSummaryFrontier(nodeIDs ids.NodeIDSet, requestID uint32) { // Note that this timeout duration won't exactly match the one that gets // registered. That's OK. @@ -99,24 +120,37 @@ func (s *sender) SendGetStateSummaryFrontier(nodeIDs ids.NodeIDSet, requestID ui s.router.RegisterRequest(nodeID, s.ctx.ChainID, requestID, message.StateSummaryFrontier) } + msgCreator := s.getMsgCreator() + // Sending a message to myself. No need to send it over the network. // Just put it right into the router. Asynchronously to avoid deadlock. if nodeIDs.Contains(s.ctx.NodeID) { nodeIDs.Remove(s.ctx.NodeID) - inMsg := s.msgCreator.InboundGetStateSummaryFrontier(s.ctx.ChainID, requestID, deadline, s.ctx.NodeID) + inMsg := msgCreator.InboundGetStateSummaryFrontier(s.ctx.ChainID, requestID, deadline, s.ctx.NodeID) go s.router.HandleInbound(inMsg) } // Create the outbound message. - outMsg, err := s.msgCreator.GetStateSummaryFrontier(s.ctx.ChainID, requestID, deadline) - s.ctx.Log.AssertNoError(err) + outMsg, err := msgCreator.GetStateSummaryFrontier(s.ctx.ChainID, requestID, deadline) // Send the message over the network. - sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()) + var sentTo ids.NodeIDSet + if err == nil { + sentTo = s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()) + } else { + s.ctx.Log.Error("failed to build message", + zap.Stringer("messageOp", message.GetStateSummaryFrontier), + zap.Stringer("chainID", s.ctx.ChainID), + zap.Uint32("requestID", requestID), + zap.Duration("deadline", deadline), + zap.Error(err), + ) + } + for nodeID := range nodeIDs { if !sentTo.Contains(nodeID) { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.GetStateSummaryFrontier), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -126,15 +160,17 @@ func (s *sender) SendGetStateSummaryFrontier(nodeIDs ids.NodeIDSet, requestID ui } func (s *sender) SendStateSummaryFrontier(nodeID ids.NodeID, requestID uint32, summary []byte) { + msgCreator := s.getMsgCreator() + // Sending this message to myself. if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InboundStateSummaryFrontier(s.ctx.ChainID, requestID, summary, nodeID) + inMsg := msgCreator.InboundStateSummaryFrontier(s.ctx.ChainID, requestID, summary, nodeID) go s.router.HandleInbound(inMsg) return } // Create the outbound message. - outMsg, err := s.msgCreator.StateSummaryFrontier(s.ctx.ChainID, requestID, summary) + outMsg, err := msgCreator.StateSummaryFrontier(s.ctx.ChainID, requestID, summary) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.StateSummaryFrontier), @@ -151,13 +187,13 @@ func (s *sender) SendStateSummaryFrontier(nodeID ids.NodeID, requestID uint32, s nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.StateSummaryFrontier), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), ) s.ctx.Log.Verbo("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.StateSummaryFrontier), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -180,16 +216,18 @@ func (s *sender) SendGetAcceptedStateSummary(nodeIDs ids.NodeIDSet, requestID ui s.router.RegisterRequest(nodeID, s.ctx.ChainID, requestID, message.AcceptedStateSummary) } + msgCreator := s.getMsgCreator() + // Sending a message to myself. No need to send it over the network. // Just put it right into the router. Asynchronously to avoid deadlock. if nodeIDs.Contains(s.ctx.NodeID) { nodeIDs.Remove(s.ctx.NodeID) - inMsg := s.msgCreator.InboundGetAcceptedStateSummary(s.ctx.ChainID, requestID, heights, deadline, s.ctx.NodeID) + inMsg := msgCreator.InboundGetAcceptedStateSummary(s.ctx.ChainID, requestID, heights, deadline, s.ctx.NodeID) go s.router.HandleInbound(inMsg) } // Create the outbound message. - outMsg, err := s.msgCreator.GetAcceptedStateSummary(s.ctx.ChainID, requestID, deadline, heights) + outMsg, err := msgCreator.GetAcceptedStateSummary(s.ctx.ChainID, requestID, deadline, heights) // Send the message over the network. var sentTo ids.NodeIDSet @@ -219,14 +257,16 @@ func (s *sender) SendGetAcceptedStateSummary(nodeIDs ids.NodeIDSet, requestID ui } func (s *sender) SendAcceptedStateSummary(nodeID ids.NodeID, requestID uint32, summaryIDs []ids.ID) { + msgCreator := s.getMsgCreator() + if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InboundAcceptedStateSummary(s.ctx.ChainID, requestID, summaryIDs, nodeID) + inMsg := msgCreator.InboundAcceptedStateSummary(s.ctx.ChainID, requestID, summaryIDs, nodeID) go s.router.HandleInbound(inMsg) return } // Create the outbound message. - outMsg, err := s.msgCreator.AcceptedStateSummary(s.ctx.ChainID, requestID, summaryIDs) + outMsg, err := msgCreator.AcceptedStateSummary(s.ctx.ChainID, requestID, summaryIDs) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.AcceptedStateSummary), @@ -243,7 +283,7 @@ func (s *sender) SendAcceptedStateSummary(nodeID ids.NodeID, requestID uint32, s nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AcceptedStateSummary), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -266,24 +306,37 @@ func (s *sender) SendGetAcceptedFrontier(nodeIDs ids.NodeIDSet, requestID uint32 s.router.RegisterRequest(nodeID, s.ctx.ChainID, requestID, message.AcceptedFrontier) } + msgCreator := s.getMsgCreator() + // Sending a message to myself. No need to send it over the network. // Just put it right into the router. Asynchronously to avoid deadlock. if nodeIDs.Contains(s.ctx.NodeID) { nodeIDs.Remove(s.ctx.NodeID) - inMsg := s.msgCreator.InboundGetAcceptedFrontier(s.ctx.ChainID, requestID, deadline, s.ctx.NodeID) + inMsg := msgCreator.InboundGetAcceptedFrontier(s.ctx.ChainID, requestID, deadline, s.ctx.NodeID) go s.router.HandleInbound(inMsg) } // Create the outbound message. - outMsg, err := s.msgCreator.GetAcceptedFrontier(s.ctx.ChainID, requestID, deadline) - s.ctx.Log.AssertNoError(err) + outMsg, err := msgCreator.GetAcceptedFrontier(s.ctx.ChainID, requestID, deadline) // Send the message over the network. - sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()) + var sentTo ids.NodeIDSet + if err == nil { + sentTo = s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()) + } else { + s.ctx.Log.Error("failed to build message", + zap.Stringer("messageOp", message.GetAcceptedFrontier), + zap.Stringer("chainID", s.ctx.ChainID), + zap.Uint32("requestID", requestID), + zap.Duration("deadline", deadline), + zap.Error(err), + ) + } + for nodeID := range nodeIDs { if !sentTo.Contains(nodeID) { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.GetAcceptedFrontier), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -293,15 +346,17 @@ func (s *sender) SendGetAcceptedFrontier(nodeIDs ids.NodeIDSet, requestID uint32 } func (s *sender) SendAcceptedFrontier(nodeID ids.NodeID, requestID uint32, containerIDs []ids.ID) { + msgCreator := s.getMsgCreator() + // Sending this message to myself. if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InboundAcceptedFrontier(s.ctx.ChainID, requestID, containerIDs, nodeID) + inMsg := msgCreator.InboundAcceptedFrontier(s.ctx.ChainID, requestID, containerIDs, nodeID) go s.router.HandleInbound(inMsg) return } // Create the outbound message. - outMsg, err := s.msgCreator.AcceptedFrontier(s.ctx.ChainID, requestID, containerIDs) + outMsg, err := msgCreator.AcceptedFrontier(s.ctx.ChainID, requestID, containerIDs) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.AcceptedFrontier), @@ -318,7 +373,7 @@ func (s *sender) SendAcceptedFrontier(nodeID ids.NodeID, requestID uint32, conta nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AcceptedFrontier), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -341,16 +396,18 @@ func (s *sender) SendGetAccepted(nodeIDs ids.NodeIDSet, requestID uint32, contai s.router.RegisterRequest(nodeID, s.ctx.ChainID, requestID, message.Accepted) } + msgCreator := s.getMsgCreator() + // Sending a message to myself. No need to send it over the network. // Just put it right into the router. Asynchronously to avoid deadlock. if nodeIDs.Contains(s.ctx.NodeID) { nodeIDs.Remove(s.ctx.NodeID) - inMsg := s.msgCreator.InboundGetAccepted(s.ctx.ChainID, requestID, deadline, containerIDs, s.ctx.NodeID) + inMsg := msgCreator.InboundGetAccepted(s.ctx.ChainID, requestID, deadline, containerIDs, s.ctx.NodeID) go s.router.HandleInbound(inMsg) } // Create the outbound message. - outMsg, err := s.msgCreator.GetAccepted(s.ctx.ChainID, requestID, deadline, containerIDs) + outMsg, err := msgCreator.GetAccepted(s.ctx.ChainID, requestID, deadline, containerIDs) // Send the message over the network. var sentTo ids.NodeIDSet @@ -380,14 +437,16 @@ func (s *sender) SendGetAccepted(nodeIDs ids.NodeIDSet, requestID uint32, contai } func (s *sender) SendAccepted(nodeID ids.NodeID, requestID uint32, containerIDs []ids.ID) { + msgCreator := s.getMsgCreator() + if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InboundAccepted(s.ctx.ChainID, requestID, containerIDs, nodeID) + inMsg := msgCreator.InboundAccepted(s.ctx.ChainID, requestID, containerIDs, nodeID) go s.router.HandleInbound(inMsg) return } // Create the outbound message. - outMsg, err := s.msgCreator.Accepted(s.ctx.ChainID, requestID, containerIDs) + outMsg, err := msgCreator.Accepted(s.ctx.ChainID, requestID, containerIDs) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.Accepted), @@ -404,7 +463,7 @@ func (s *sender) SendAccepted(nodeID ids.NodeID, requestID uint32, containerIDs nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Accepted), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -418,9 +477,11 @@ func (s *sender) SendGetAncestors(nodeID ids.NodeID, requestID uint32, container // that we won't get a response from this node. s.router.RegisterRequest(nodeID, s.ctx.ChainID, requestID, message.Ancestors) + msgCreator := s.getMsgCreator() + // Sending a GetAncestors to myself always fails. if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) return } @@ -430,7 +491,7 @@ func (s *sender) SendGetAncestors(nodeID ids.NodeID, requestID uint32, container if s.timeouts.IsBenched(nodeID, s.ctx.ChainID) { s.failedDueToBench[message.GetAncestors].Inc() // update metric s.timeouts.RegisterRequestToUnreachableValidator() - inMsg := s.msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) return } @@ -439,7 +500,7 @@ func (s *sender) SendGetAncestors(nodeID ids.NodeID, requestID uint32, container // registered. That's OK. deadline := s.timeouts.TimeoutDuration() // Create the outbound message. - outMsg, err := s.msgCreator.GetAncestors(s.ctx.ChainID, requestID, deadline, containerID) + outMsg, err := msgCreator.GetAncestors(s.ctx.ChainID, requestID, deadline, containerID) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.GetAncestors), @@ -449,7 +510,7 @@ func (s *sender) SendGetAncestors(nodeID ids.NodeID, requestID uint32, container zap.Error(err), ) - inMsg := s.msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) return } @@ -459,7 +520,7 @@ func (s *sender) SendGetAncestors(nodeID ids.NodeID, requestID uint32, container nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.GetAncestors), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -467,7 +528,7 @@ func (s *sender) SendGetAncestors(nodeID ids.NodeID, requestID uint32, container ) s.timeouts.RegisterRequestToUnreachableValidator() - inMsg := s.msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.GetAncestorsFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } @@ -476,8 +537,10 @@ func (s *sender) SendGetAncestors(nodeID ids.NodeID, requestID uint32, container // on the specified node. // The Ancestors message gives the recipient the contents of several containers. func (s *sender) SendAncestors(nodeID ids.NodeID, requestID uint32, containers [][]byte) { + msgCreator := s.getMsgCreator() + // Create the outbound message. - outMsg, err := s.msgCreator.Ancestors(s.ctx.ChainID, requestID, containers) + outMsg, err := msgCreator.Ancestors(s.ctx.ChainID, requestID, containers) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.Ancestors), @@ -494,7 +557,7 @@ func (s *sender) SendAncestors(nodeID ids.NodeID, requestID uint32, containers [ nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Ancestors), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -512,9 +575,11 @@ func (s *sender) SendGet(nodeID ids.NodeID, requestID uint32, containerID ids.ID // that we won't get a response from this node. s.router.RegisterRequest(nodeID, s.ctx.ChainID, requestID, message.Put) + msgCreator := s.getMsgCreator() + // Sending a Get to myself always fails. if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InternalFailedRequest(message.GetFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.GetFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) return } @@ -524,7 +589,7 @@ func (s *sender) SendGet(nodeID ids.NodeID, requestID uint32, containerID ids.ID if s.timeouts.IsBenched(nodeID, s.ctx.ChainID) { s.failedDueToBench[message.Get].Inc() // update metric s.timeouts.RegisterRequestToUnreachableValidator() - inMsg := s.msgCreator.InternalFailedRequest(message.GetFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.GetFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) return } @@ -533,15 +598,28 @@ func (s *sender) SendGet(nodeID ids.NodeID, requestID uint32, containerID ids.ID // registered. That's OK. deadline := s.timeouts.TimeoutDuration() // Create the outbound message. - outMsg, err := s.msgCreator.Get(s.ctx.ChainID, requestID, deadline, containerID) - s.ctx.Log.AssertNoError(err) + outMsg, err := msgCreator.Get(s.ctx.ChainID, requestID, deadline, containerID) // Send the message over the network. - nodeIDs := ids.NewNodeIDSet(1) - nodeIDs.Add(nodeID) - if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { + var sentTo ids.NodeIDSet + if err == nil { + nodeIDs := ids.NewNodeIDSet(1) + nodeIDs.Add(nodeID) + sentTo = s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()) + } else { + s.ctx.Log.Error("failed to build message", + zap.Stringer("messageOp", message.Get), + zap.Stringer("chainID", s.ctx.ChainID), + zap.Uint32("requestID", requestID), + zap.Duration("deadline", deadline), + zap.Stringer("containerID", containerID), + zap.Error(err), + ) + } + + if sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Get), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -549,7 +627,7 @@ func (s *sender) SendGet(nodeID ids.NodeID, requestID uint32, containerID ids.ID ) s.timeouts.RegisterRequestToUnreachableValidator() - inMsg := s.msgCreator.InternalFailedRequest(message.GetFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.GetFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } @@ -558,15 +636,16 @@ func (s *sender) SendGet(nodeID ids.NodeID, requestID uint32, containerID ids.ID // on the specified node. // The Put message signifies that this consensus engine is giving to the recipient // the contents of the specified container. -func (s *sender) SendPut(nodeID ids.NodeID, requestID uint32, containerID ids.ID, container []byte) { +func (s *sender) SendPut(nodeID ids.NodeID, requestID uint32, container []byte) { + msgCreator := s.getMsgCreator() + // Create the outbound message. - outMsg, err := s.msgCreator.Put(s.ctx.ChainID, requestID, containerID, container) + outMsg, err := msgCreator.Put(s.ctx.ChainID, requestID, container) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.Put), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), zap.Error(err), ) @@ -578,18 +657,16 @@ func (s *sender) SendPut(nodeID ids.NodeID, requestID uint32, containerID ids.ID nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Put), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", containerID), ) s.ctx.Log.Verbo("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Put), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), ) } @@ -599,7 +676,7 @@ func (s *sender) SendPut(nodeID ids.NodeID, requestID uint32, containerID ids.ID // on the specified nodes. // The PushQuery message signifies that this consensus engine would like each node to send // their preferred frontier given the existence of the specified container. -func (s *sender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, containerID ids.ID, container []byte) { +func (s *sender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, container []byte) { // Tell the router to expect a response message or a message notifying // that we won't get a response from each of these nodes. // We register timeouts for all nodes, regardless of whether we fail @@ -613,11 +690,13 @@ func (s *sender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe // registered. That's OK. deadline := s.timeouts.TimeoutDuration() + msgCreator := s.getMsgCreator() + // Sending a message to myself. No need to send it over the network. // Just put it right into the router. Do so asynchronously to avoid deadlock. if nodeIDs.Contains(s.ctx.NodeID) { nodeIDs.Remove(s.ctx.NodeID) - inMsg := s.msgCreator.InboundPushQuery(s.ctx.ChainID, requestID, deadline, containerID, container, s.ctx.NodeID) + inMsg := msgCreator.InboundPushQuery(s.ctx.ChainID, requestID, deadline, container, s.ctx.NodeID) go s.router.HandleInbound(inMsg) } @@ -630,14 +709,14 @@ func (s *sender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe s.timeouts.RegisterRequestToUnreachableValidator() // Immediately register a failure. Do so asynchronously to avoid deadlock. - inMsg := s.msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } // Create the outbound message. // [sentTo] are the IDs of validators who may receive the message. - outMsg, err := s.msgCreator.PushQuery(s.ctx.ChainID, requestID, deadline, containerID, container) + outMsg, err := msgCreator.PushQuery(s.ctx.ChainID, requestID, deadline, container) // Send the message over the network. var sentTo ids.NodeIDSet @@ -648,7 +727,6 @@ func (s *sender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe zap.Stringer("messageOp", message.PushQuery), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), zap.Error(err), ) @@ -661,20 +739,18 @@ func (s *sender) SendPushQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", containerID), ) s.ctx.Log.Verbo("failed to send message", zap.Stringer("messageOp", message.PushQuery), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), ) // Register failures for nodes we didn't send a request to. s.timeouts.RegisterRequestToUnreachableValidator() - inMsg := s.msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } @@ -698,11 +774,13 @@ func (s *sender) SendPullQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe // registered. That's OK. deadline := s.timeouts.TimeoutDuration() + msgCreator := s.getMsgCreator() + // Sending a message to myself. No need to send it over the network. // Just put it right into the router. Do so asynchronously to avoid deadlock. if nodeIDs.Contains(s.ctx.NodeID) { nodeIDs.Remove(s.ctx.NodeID) - inMsg := s.msgCreator.InboundPullQuery(s.ctx.ChainID, requestID, deadline, containerID, s.ctx.NodeID) + inMsg := msgCreator.InboundPullQuery(s.ctx.ChainID, requestID, deadline, containerID, s.ctx.NodeID) go s.router.HandleInbound(inMsg) } @@ -714,22 +792,33 @@ func (s *sender) SendPullQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe nodeIDs.Remove(nodeID) s.timeouts.RegisterRequestToUnreachableValidator() // Immediately register a failure. Do so asynchronously to avoid deadlock. - inMsg := s.msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } // Create the outbound message. - outMsg, err := s.msgCreator.PullQuery(s.ctx.ChainID, requestID, deadline, containerID) - s.ctx.Log.AssertNoError(err) + outMsg, err := msgCreator.PullQuery(s.ctx.ChainID, requestID, deadline, containerID) // Send the message over the network. - sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()) + var sentTo ids.NodeIDSet + if err == nil { + sentTo = s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()) + } else { + s.ctx.Log.Error("failed to build message", + zap.Stringer("messageOp", message.PullQuery), + zap.Stringer("chainID", s.ctx.ChainID), + zap.Uint32("requestID", requestID), + zap.Duration("deadline", deadline), + zap.Stringer("containerID", containerID), + zap.Error(err), + ) + } for nodeID := range nodeIDs { if !sentTo.Contains(nodeID) { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.PullQuery), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -738,7 +827,7 @@ func (s *sender) SendPullQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe // Register failures for nodes we didn't send a request to. s.timeouts.RegisterRequestToUnreachableValidator() - inMsg := s.msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.QueryFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } @@ -746,59 +835,23 @@ func (s *sender) SendPullQuery(nodeIDs ids.NodeIDSet, requestID uint32, containe // SendChits sends chits func (s *sender) SendChits(nodeID ids.NodeID, requestID uint32, votes []ids.ID) { - // If [nodeID] is myself, send this message directly - // to my own router rather than sending it over the network - if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InboundChits(s.ctx.ChainID, requestID, votes, nodeID) - go s.router.HandleInbound(inMsg) - return - } - - // Create the outbound message. - outMsg, err := s.msgCreator.Chits(s.ctx.ChainID, requestID, votes) - if err != nil { - s.ctx.Log.Error("failed to build message", - zap.Stringer("messageOp", message.Chits), - zap.Stringer("chainID", s.ctx.ChainID), - zap.Uint32("requestID", requestID), - zap.Stringer("containerIDs", ids.SliceStringer(votes)), - zap.Error(err), - ) - return - } - - // Send the message over the network. - nodeIDs := ids.NewNodeIDSet(1) - nodeIDs.Add(nodeID) - if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { - s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), - zap.Stringer("nodeID", nodeID), - zap.Stringer("chainID", s.ctx.ChainID), - zap.Uint32("requestID", requestID), - zap.Stringer("containerIDs", ids.SliceStringer(votes)), - ) - } -} + msgCreator := s.getMsgCreator() -// SendChitsV2 sends chits V2 -func (s *sender) SendChitsV2(nodeID ids.NodeID, requestID uint32, votes []ids.ID, vote ids.ID) { // If [nodeID] is myself, send this message directly // to my own router rather than sending it over the network if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InboundChitsV2(s.ctx.ChainID, requestID, votes, vote, nodeID) + inMsg := msgCreator.InboundChits(s.ctx.ChainID, requestID, votes, nodeID) go s.router.HandleInbound(inMsg) return } // Create the outbound message. - outMsg, err := s.msgCreator.ChitsV2(s.ctx.ChainID, requestID, votes, vote) + outMsg, err := msgCreator.Chits(s.ctx.ChainID, requestID, votes) if err != nil { s.ctx.Log.Error("failed to build message", - zap.Stringer("messageOp", message.ChitsV2), + zap.Stringer("messageOp", message.Chits), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", vote), zap.Stringer("containerIDs", ids.SliceStringer(votes)), zap.Error(err), ) @@ -810,11 +863,10 @@ func (s *sender) SendChitsV2(nodeID ids.NodeID, requestID uint32, votes []ids.ID nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Chits), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), - zap.Stringer("containerID", vote), zap.Stringer("containerIDs", ids.SliceStringer(votes)), ) } @@ -836,11 +888,13 @@ func (s *sender) SendAppRequest(nodeIDs ids.NodeIDSet, requestID uint32, appRequ // registered. That's OK. deadline := s.timeouts.TimeoutDuration() + msgCreator := s.getMsgCreator() + // Sending a message to myself. No need to send it over the network. // Just put it right into the router. Do so asynchronously to avoid deadlock. if nodeIDs.Contains(s.ctx.NodeID) { nodeIDs.Remove(s.ctx.NodeID) - inMsg := s.msgCreator.InboundAppRequest(s.ctx.ChainID, requestID, deadline, appRequestBytes, s.ctx.NodeID) + inMsg := msgCreator.InboundAppRequest(s.ctx.ChainID, requestID, deadline, appRequestBytes, s.ctx.NodeID) go s.router.HandleInbound(inMsg) } @@ -853,14 +907,14 @@ func (s *sender) SendAppRequest(nodeIDs ids.NodeIDSet, requestID uint32, appRequ s.timeouts.RegisterRequestToUnreachableValidator() // Immediately register a failure. Do so asynchronously to avoid deadlock. - inMsg := s.msgCreator.InternalFailedRequest(message.AppRequestFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.AppRequestFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } // Create the outbound message. // [sentTo] are the IDs of nodes who may receive the message. - outMsg, err := s.msgCreator.AppRequest(s.ctx.ChainID, requestID, deadline, appRequestBytes) + outMsg, err := msgCreator.AppRequest(s.ctx.ChainID, requestID, deadline, appRequestBytes) // Send the message over the network. var sentTo ids.NodeIDSet @@ -894,7 +948,7 @@ func (s *sender) SendAppRequest(nodeIDs ids.NodeIDSet, requestID uint32, appRequ // Register failures for nodes we didn't send a request to. s.timeouts.RegisterRequestToUnreachableValidator() - inMsg := s.msgCreator.InternalFailedRequest(message.AppRequestFailed, nodeID, s.ctx.ChainID, requestID) + inMsg := msgCreator.InternalFailedRequest(message.AppRequestFailed, nodeID, s.ctx.ChainID, requestID) go s.router.HandleInbound(inMsg) } } @@ -904,14 +958,16 @@ func (s *sender) SendAppRequest(nodeIDs ids.NodeIDSet, requestID uint32, appRequ // SendAppResponse sends a response to an application-level request from the // given node func (s *sender) SendAppResponse(nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error { + msgCreator := s.getMsgCreator() + if nodeID == s.ctx.NodeID { - inMsg := s.msgCreator.InboundAppResponse(s.ctx.ChainID, requestID, appResponseBytes, nodeID) + inMsg := msgCreator.InboundAppResponse(s.ctx.ChainID, requestID, appResponseBytes, nodeID) go s.router.HandleInbound(inMsg) return nil } // Create the outbound message. - outMsg, err := s.msgCreator.AppResponse(s.ctx.ChainID, requestID, appResponseBytes) + outMsg, err := msgCreator.AppResponse(s.ctx.ChainID, requestID, appResponseBytes) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.AppResponse), @@ -928,13 +984,13 @@ func (s *sender) SendAppResponse(nodeID ids.NodeID, requestID uint32, appRespons nodeIDs.Add(nodeID) if sentTo := s.sender.Send(outMsg, nodeIDs, s.ctx.SubnetID, s.ctx.IsValidatorOnly()); sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AppResponse), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), ) s.ctx.Log.Verbo("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AppResponse), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Uint32("requestID", requestID), @@ -945,8 +1001,10 @@ func (s *sender) SendAppResponse(nodeID ids.NodeID, requestID uint32, appRespons } func (s *sender) SendAppGossipSpecific(nodeIDs ids.NodeIDSet, appGossipBytes []byte) error { + msgCreator := s.getMsgCreator() + // Create the outbound message. - outMsg, err := s.msgCreator.AppGossip(s.ctx.ChainID, appGossipBytes) + outMsg, err := msgCreator.AppGossip(s.ctx.ChainID, appGossipBytes) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.AppGossip), @@ -962,12 +1020,12 @@ func (s *sender) SendAppGossipSpecific(nodeIDs ids.NodeIDSet, appGossipBytes []b for nodeID := range nodeIDs { if !sentTo.Contains(nodeID) { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AppGossip), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), ) s.ctx.Log.Verbo("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AppGossip), zap.Stringer("nodeID", nodeID), zap.Stringer("chainID", s.ctx.ChainID), zap.Binary("payload", appGossipBytes), @@ -980,8 +1038,10 @@ func (s *sender) SendAppGossipSpecific(nodeIDs ids.NodeIDSet, appGossipBytes []b // SendAppGossip sends an application-level gossip message. func (s *sender) SendAppGossip(appGossipBytes []byte) error { + msgCreator := s.getMsgCreator() + // Create the outbound message. - outMsg, err := s.msgCreator.AppGossip(s.ctx.ChainID, appGossipBytes) + outMsg, err := msgCreator.AppGossip(s.ctx.ChainID, appGossipBytes) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.AppGossip), @@ -999,11 +1059,11 @@ func (s *sender) SendAppGossip(appGossipBytes []byte) error { sentTo := s.sender.Gossip(outMsg, s.ctx.SubnetID, s.ctx.IsValidatorOnly(), validatorSize, nonValidatorSize, peerSize) if sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AppGossip), zap.Stringer("chainID", s.ctx.ChainID), ) s.ctx.Log.Verbo("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.AppGossip), zap.Stringer("chainID", s.ctx.ChainID), zap.Binary("payload", appGossipBytes), ) @@ -1012,14 +1072,15 @@ func (s *sender) SendAppGossip(appGossipBytes []byte) error { } // SendGossip gossips the provided container -func (s *sender) SendGossip(containerID ids.ID, container []byte) { +func (s *sender) SendGossip(container []byte) { + msgCreator := s.getMsgCreator() + // Create the outbound message. - outMsg, err := s.msgCreator.Put(s.ctx.ChainID, constants.GossipMsgRequestID, containerID, container) + outMsg, err := msgCreator.Put(s.ctx.ChainID, constants.GossipMsgRequestID, container) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.Put), zap.Stringer("chainID", s.ctx.ChainID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), zap.Error(err), ) @@ -1036,33 +1097,32 @@ func (s *sender) SendGossip(containerID ids.ID, container []byte) { ) if sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Put), zap.Stringer("chainID", s.ctx.ChainID), - zap.Stringer("containerID", containerID), ) s.ctx.Log.Verbo("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Put), zap.Stringer("chainID", s.ctx.ChainID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), ) } } // Accept is called after every consensus decision -func (s *sender) Accept(ctx *snow.ConsensusContext, containerID ids.ID, container []byte) error { +func (s *sender) Accept(ctx *snow.ConsensusContext, _ ids.ID, container []byte) error { if ctx.GetState() != snow.NormalOp { // don't gossip during bootstrapping return nil } + msgCreator := s.getMsgCreator() + // Create the outbound message. - outMsg, err := s.msgCreator.Put(s.ctx.ChainID, constants.GossipMsgRequestID, containerID, container) + outMsg, err := msgCreator.Put(s.ctx.ChainID, constants.GossipMsgRequestID, container) if err != nil { s.ctx.Log.Error("failed to build message", zap.Stringer("messageOp", message.Put), zap.Stringer("chainID", s.ctx.ChainID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), zap.Error(err), ) @@ -1079,14 +1139,12 @@ func (s *sender) Accept(ctx *snow.ConsensusContext, containerID ids.ID, containe ) if sentTo.Len() == 0 { s.ctx.Log.Debug("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Put), zap.Stringer("chainID", s.ctx.ChainID), - zap.Stringer("containerID", containerID), ) s.ctx.Log.Verbo("failed to send message", - zap.Stringer("messageOp", outMsg.Op()), + zap.Stringer("messageOp", message.Put), zap.Stringer("chainID", s.ctx.ChainID), - zap.Stringer("containerID", containerID), zap.Binary("container", container), ) } diff --git a/avalanchego/snow/networking/sender/sender_test.go b/avalanchego/snow/networking/sender/sender_test.go index 0e9bf2d9..7171f86e 100644 --- a/avalanchego/snow/networking/sender/sender_test.go +++ b/avalanchego/snow/networking/sender/sender_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sender @@ -54,15 +54,17 @@ func TestTimeout(t *testing.T) { "", prometheus.NewRegistry(), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) go tm.Dispatch() chainRouter := router.ChainRouter{} + metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) + mcProto, err := message.NewCreatorWithProto(metrics, "dummyNamespace", true, 10*time.Second) + require.NoError(t, err) + err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, tm, time.Second, ids.Set{}, ids.Set{}, nil, router.HealthConfig{}, "", prometheus.NewRegistry()) require.NoError(t, err) @@ -70,7 +72,7 @@ func TestTimeout(t *testing.T) { externalSender := &ExternalSenderTest{TB: t} externalSender.Default(false) - sender, err := New(context, mc, externalSender, &chainRouter, tm, defaultGossipConfig) + sender, err := New(context, mc, mcProto, time.Now().Add(time.Hour) /* TODO: test with banff accepted */, externalSender, &chainRouter, tm, defaultGossipConfig) require.NoError(t, err) wg := sync.WaitGroup{} @@ -145,15 +147,18 @@ func TestReliableMessages(t *testing.T) { "", prometheus.NewRegistry(), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + go tm.Dispatch() chainRouter := router.ChainRouter{} + metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) + mcProto, err := message.NewCreatorWithProto(metrics, "dummyNamespace", true, 10*time.Second) + require.NoError(t, err) + err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, tm, time.Second, ids.Set{}, ids.Set{}, nil, router.HealthConfig{}, "", prometheus.NewRegistry()) require.NoError(t, err) @@ -162,7 +167,7 @@ func TestReliableMessages(t *testing.T) { externalSender := &ExternalSenderTest{TB: t} externalSender.Default(false) - sender, err := New(context, mc, externalSender, &chainRouter, tm, defaultGossipConfig) + sender, err := New(context, mc, mcProto, time.Now().Add(time.Hour) /* TODO: test with banff accepted */, externalSender, &chainRouter, tm, defaultGossipConfig) require.NoError(t, err) ctx := snow.DefaultConsensusContextTest() @@ -241,15 +246,18 @@ func TestReliableMessagesToMyself(t *testing.T) { "", prometheus.NewRegistry(), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + go tm.Dispatch() chainRouter := router.ChainRouter{} + metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) require.NoError(t, err) + mcProto, err := message.NewCreatorWithProto(metrics, "dummyNamespace", true, 10*time.Second) + require.NoError(t, err) + err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, tm, time.Second, ids.Set{}, ids.Set{}, nil, router.HealthConfig{}, "", prometheus.NewRegistry()) require.NoError(t, err) @@ -258,7 +266,7 @@ func TestReliableMessagesToMyself(t *testing.T) { externalSender := &ExternalSenderTest{TB: t} externalSender.Default(false) - sender, err := New(context, mc, externalSender, &chainRouter, tm, defaultGossipConfig) + sender, err := New(context, mc, mcProto, time.Now().Add(time.Hour) /* TODO: test with banff accepted */, externalSender, &chainRouter, tm, defaultGossipConfig) require.NoError(t, err) ctx := snow.DefaultConsensusContextTest() diff --git a/avalanchego/snow/networking/sender/test_external_sender.go b/avalanchego/snow/networking/sender/test_external_sender.go index 072a1b3d..03b497a1 100644 --- a/avalanchego/snow/networking/sender/test_external_sender.go +++ b/avalanchego/snow/networking/sender/test_external_sender.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sender diff --git a/avalanchego/snow/networking/timeout/manager.go b/avalanchego/snow/networking/timeout/manager.go index 1041be61..bf78e4bf 100644 --- a/avalanchego/snow/networking/timeout/manager.go +++ b/avalanchego/snow/networking/timeout/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timeout diff --git a/avalanchego/snow/networking/timeout/manager_test.go b/avalanchego/snow/networking/timeout/manager_test.go index 992233f2..1bc52273 100644 --- a/avalanchego/snow/networking/timeout/manager_test.go +++ b/avalanchego/snow/networking/timeout/manager_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timeout diff --git a/avalanchego/snow/networking/timeout/metrics.go b/avalanchego/snow/networking/timeout/metrics.go index 5391f542..e650b8fd 100644 --- a/avalanchego/snow/networking/timeout/metrics.go +++ b/avalanchego/snow/networking/timeout/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timeout diff --git a/avalanchego/snow/networking/tracker/resource_tracker.go b/avalanchego/snow/networking/tracker/resource_tracker.go index f3272cd5..b6758c4d 100644 --- a/avalanchego/snow/networking/tracker/resource_tracker.go +++ b/avalanchego/snow/networking/tracker/resource_tracker.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package tracker @@ -72,7 +72,7 @@ func (t *cpuResourceTracker) Usage(nodeID ids.NodeID, now time.Time) float64 { return 0 } - portionUsageByNode := m.(meter.Meter).Read(now) / measuredProcessingTime + portionUsageByNode := m.Read(now) / measuredProcessingTime return realCPUUsage * portionUsageByNode } @@ -107,7 +107,7 @@ func (t *cpuResourceTracker) TimeUntilUsage(nodeID ids.NodeID, now time.Time, va } scale := realCPUUsage / measuredProcessingTime - return m.(meter.Meter).TimeUntil(now, value/scale) + return m.TimeUntil(now, value/scale) } type diskResourceTracker struct { @@ -136,7 +136,7 @@ func (t *diskResourceTracker) Usage(nodeID ids.NodeID, now time.Time) float64 { return 0 } - portionUsageByNode := m.(meter.Meter).Read(now) / measuredProcessingTime + portionUsageByNode := m.Read(now) / measuredProcessingTime return realReadUsage * portionUsageByNode } @@ -184,7 +184,7 @@ func (t *diskResourceTracker) TimeUntilUsage(nodeID ids.NodeID, now time.Time, v } scale := realReadUsage / measuredProcessingTime - return m.(meter.Meter).TimeUntil(now, value/scale) + return m.TimeUntil(now, value/scale) } type resourceTracker struct { @@ -200,7 +200,7 @@ type resourceTracker struct { // utilized. This doesn't necessarily result in the meters being sorted // based on their usage. However, in practice the nodes that are not being // utilized will move towards the oldest elements where they can be deleted. - meters linkedhashmap.LinkedHashmap + meters linkedhashmap.LinkedHashmap[ids.NodeID, meter.Meter] metrics *trackerMetrics } @@ -215,7 +215,7 @@ func NewResourceTracker( resources: resources, processingMeter: factory.New(halflife), halflife: halflife, - meters: linkedhashmap.New(), + meters: linkedhashmap.New[ids.NodeID, meter.Meter](), } var err error t.metrics, err = newCPUTrackerMetrics("resource_tracker", reg) @@ -257,7 +257,7 @@ func (rt *resourceTracker) StopProcessing(nodeID ids.NodeID, now time.Time) { func (rt *resourceTracker) getMeter(nodeID ids.NodeID) meter.Meter { m, exists := rt.meters.Get(nodeID) if exists { - return m.(meter.Meter) + return m } newMeter := rt.factory.New(rt.halflife) @@ -272,12 +272,11 @@ func (rt *resourceTracker) getMeter(nodeID ids.NodeID) meter.Meter { // doesn't guarantee that all meters showing less than [epsilon] are removed. func (rt *resourceTracker) prune(now time.Time) { for { - oldest, meterIntf, exists := rt.meters.Oldest() + oldest, meter, exists := rt.meters.Oldest() if !exists { return } - meter := meterIntf.(meter.Meter) if meter.Read(now) > epsilon { return } diff --git a/avalanchego/snow/networking/tracker/resource_tracker_test.go b/avalanchego/snow/networking/tracker/resource_tracker_test.go index e9c0e35d..c7a6bc68 100644 --- a/avalanchego/snow/networking/tracker/resource_tracker_test.go +++ b/avalanchego/snow/networking/tracker/resource_tracker_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package tracker diff --git a/avalanchego/snow/networking/tracker/targeter_test.go b/avalanchego/snow/networking/tracker/targeter_test.go index a0f25f65..1f363c6b 100644 --- a/avalanchego/snow/networking/tracker/targeter_test.go +++ b/avalanchego/snow/networking/tracker/targeter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package tracker diff --git a/avalanchego/snow/networking/worker/pool.go b/avalanchego/snow/networking/worker/pool.go index 63af7a55..8617fd4c 100644 --- a/avalanchego/snow/networking/worker/pool.go +++ b/avalanchego/snow/networking/worker/pool.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package worker diff --git a/avalanchego/snow/state.go b/avalanchego/snow/state.go index e0871de4..845819d6 100644 --- a/avalanchego/snow/state.go +++ b/avalanchego/snow/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snow diff --git a/avalanchego/snow/uptime/locked_calculator.go b/avalanchego/snow/uptime/locked_calculator.go index d803bd9c..0dd353fa 100644 --- a/avalanchego/snow/uptime/locked_calculator.go +++ b/avalanchego/snow/uptime/locked_calculator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package uptime diff --git a/avalanchego/snow/uptime/locked_calculator_test.go b/avalanchego/snow/uptime/locked_calculator_test.go index 079ccb2b..30fd1f69 100644 --- a/avalanchego/snow/uptime/locked_calculator_test.go +++ b/avalanchego/snow/uptime/locked_calculator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package uptime diff --git a/avalanchego/snow/uptime/manager.go b/avalanchego/snow/uptime/manager.go index 7a7a5cd6..2aa694f4 100644 --- a/avalanchego/snow/uptime/manager.go +++ b/avalanchego/snow/uptime/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package uptime @@ -59,7 +59,7 @@ func NewManager(state State) Manager { } func (m *manager) StartTracking(nodeIDs []ids.NodeID) error { - currentLocalTime := m.clock.UnixTime() + now := m.clock.UnixTime() for _, nodeID := range nodeIDs { upDuration, lastUpdated, err := m.state.GetUptime(nodeID) if err != nil { @@ -68,13 +68,13 @@ func (m *manager) StartTracking(nodeIDs []ids.NodeID) error { // If we are in a weird reality where time has moved backwards, then we // shouldn't modify the validator's uptime. - if currentLocalTime.Before(lastUpdated) { + if now.Before(lastUpdated) { continue } - durationOffline := currentLocalTime.Sub(lastUpdated) + durationOffline := now.Sub(lastUpdated) newUpDuration := upDuration + durationOffline - if err := m.state.SetUptime(nodeID, newUpDuration, currentLocalTime); err != nil { + if err := m.state.SetUptime(nodeID, newUpDuration, now); err != nil { return err } } @@ -83,7 +83,7 @@ func (m *manager) StartTracking(nodeIDs []ids.NodeID) error { } func (m *manager) Shutdown(nodeIDs []ids.NodeID) error { - currentLocalTime := m.clock.UnixTime() + now := m.clock.UnixTime() for _, nodeID := range nodeIDs { if _, connected := m.connections[nodeID]; connected { if err := m.Disconnect(nodeID); err != nil { @@ -99,11 +99,11 @@ func (m *manager) Shutdown(nodeIDs []ids.NodeID) error { // If we are in a weird reality where time has moved backwards, then we // shouldn't modify the validator's uptime. - if currentLocalTime.Before(lastUpdated) { + if now.Before(lastUpdated) { continue } - if err := m.state.SetUptime(nodeID, upDuration, currentLocalTime); err != nil { + if err := m.state.SetUptime(nodeID, upDuration, now); err != nil { return err } } @@ -144,16 +144,16 @@ func (m *manager) CalculateUptime(nodeID ids.NodeID) (time.Duration, time.Time, return 0, time.Time{}, err } - currentLocalTime := m.clock.UnixTime() + now := m.clock.UnixTime() // If we are in a weird reality where time has gone backwards, make sure // that we don't double count or delete any uptime. - if currentLocalTime.Before(lastUpdated) { + if now.Before(lastUpdated) { return upDuration, lastUpdated, nil } timeConnected, isConnected := m.connections[nodeID] if !isConnected { - return upDuration, currentLocalTime, nil + return upDuration, now, nil } // The time the peer connected needs to be adjusted to ensure no time period @@ -164,15 +164,15 @@ func (m *manager) CalculateUptime(nodeID ids.NodeID) (time.Duration, time.Time, // If we are in a weird reality where time has gone backwards, make sure // that we don't double count or delete any uptime. - if currentLocalTime.Before(timeConnected) { - return upDuration, currentLocalTime, nil + if now.Before(timeConnected) { + return upDuration, now, nil } // Increase the uptimes by the amount of time this node has been running // since the last time it's uptime was written to disk. - durationConnected := currentLocalTime.Sub(timeConnected) + durationConnected := now.Sub(timeConnected) newUpDuration := upDuration + durationConnected - return newUpDuration, currentLocalTime, nil + return newUpDuration, now, nil } func (m *manager) CalculateUptimePercent(nodeID ids.NodeID) (float64, error) { @@ -184,11 +184,11 @@ func (m *manager) CalculateUptimePercent(nodeID ids.NodeID) (float64, error) { } func (m *manager) CalculateUptimePercentFrom(nodeID ids.NodeID, startTime time.Time) (float64, error) { - upDuration, currentLocalTime, err := m.CalculateUptime(nodeID) + upDuration, now, err := m.CalculateUptime(nodeID) if err != nil { return 0, err } - bestPossibleUpDuration := currentLocalTime.Sub(startTime) + bestPossibleUpDuration := now.Sub(startTime) if bestPossibleUpDuration == 0 { return 1, nil } diff --git a/avalanchego/snow/uptime/manager_test.go b/avalanchego/snow/uptime/manager_test.go index 3d7d28a8..f5a9addc 100644 --- a/avalanchego/snow/uptime/manager_test.go +++ b/avalanchego/snow/uptime/manager_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package uptime diff --git a/avalanchego/snow/uptime/state.go b/avalanchego/snow/uptime/state.go index 632429a8..5b5c8ad4 100644 --- a/avalanchego/snow/uptime/state.go +++ b/avalanchego/snow/uptime/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package uptime diff --git a/avalanchego/snow/uptime/test_state.go b/avalanchego/snow/uptime/test_state.go index db817b1f..32a6da41 100644 --- a/avalanchego/snow/uptime/test_state.go +++ b/avalanchego/snow/uptime/test_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package uptime diff --git a/avalanchego/snow/validators/connector.go b/avalanchego/snow/validators/connector.go index ab889d9a..4a1d78cd 100644 --- a/avalanchego/snow/validators/connector.go +++ b/avalanchego/snow/validators/connector.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators diff --git a/avalanchego/snow/validators/custom.go b/avalanchego/snow/validators/custom.go index 6800ac98..901310ae 100644 --- a/avalanchego/snow/validators/custom.go +++ b/avalanchego/snow/validators/custom.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "strings" + "time" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/constants" @@ -17,6 +18,14 @@ const ( costonValidatorWeight = 200_000 customValidatorWeight = 200_000 customValidatorEnv = "CUSTOM_VALIDATORS" + customValidatorExpEnv = "CUSTOM_VALIDATORS_EXPIRATION" +) + +var ( + // Set dates before release + songbirdValidatorsExpTime = time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC) + costonValidatorsExpTime = time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC) + customValidatorsExpTime = time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC) ) var ( @@ -32,8 +41,12 @@ func IsDefaultValidator(vdrID ids.NodeID) bool { return defaultValidators.isValidator(vdrID) } -func InitializeDefaultValidators(networkID uint32) { - defaultValidators.initialize(networkID) +func InitializeDefaultValidators(networkID uint32, timestamp time.Time) { + defaultValidators.initialize(networkID, timestamp) +} + +func ExpiredDefaultValidators(networkID uint32, timestamp time.Time) []Validator { + return defaultValidators.expiredValidators(networkID, timestamp) } type defaultValidatorSet struct { @@ -41,7 +54,7 @@ type defaultValidatorSet struct { vdrMap map[ids.NodeID]Validator } -func (dvs *defaultValidatorSet) initialize(networkID uint32) { +func (dvs *defaultValidatorSet) initialize(networkID uint32, timestamp time.Time) { if dvs.initialized { return } @@ -49,11 +62,11 @@ func (dvs *defaultValidatorSet) initialize(networkID uint32) { var vdrs []Validator switch networkID { case constants.LocalID: - vdrs = loadCustomValidators() + vdrs = loadCustomValidators(timestamp) case constants.SongbirdID: - vdrs = loadSongbirdValidators() + vdrs = loadSongbirdValidators(timestamp) case constants.CostonID: - vdrs = loadCostonValidators() + vdrs = loadCostonValidators(timestamp) } dvs.vdrMap = make(map[ids.NodeID]Validator) for _, vdr := range vdrs { @@ -62,6 +75,28 @@ func (dvs *defaultValidatorSet) initialize(networkID uint32) { dvs.initialized = true } +func (dvs *defaultValidatorSet) expiredValidators(networkID uint32, timestamp time.Time) []Validator { + if !dvs.initialized { + panic(errNotInitialized) + } + + switch networkID { + case constants.LocalID: + if !timestamp.Before(customValidatorsExpTime) { + return dvs.list() + } + case constants.SongbirdID: + if !timestamp.Before(songbirdValidatorsExpTime) { + return dvs.list() + } + case constants.CostonID: + if !timestamp.Before(costonValidatorsExpTime) { + return dvs.list() + } + } + return nil +} + func (dvs *defaultValidatorSet) list() []Validator { if !dvs.initialized { panic(errNotInitialized) @@ -81,13 +116,26 @@ func (dvs *defaultValidatorSet) isValidator(vdrID ids.NodeID) bool { return ok } -func loadCustomValidators() []Validator { +func loadCustomValidators(timestamp time.Time) []Validator { customValidatorList := os.Getenv(customValidatorEnv) + customValidatorExpString := os.Getenv(customValidatorExpEnv) + if len(customValidatorExpString) > 0 { + if t, err := time.Parse(time.RFC3339, customValidatorExpString); err == nil { + customValidatorsExpTime = t + } + // Ignore if error occurs, use default expiration time + } + if !timestamp.Before(customValidatorsExpTime) { + return nil + } nodeIDs := strings.Split(customValidatorList, ",") return createValidators(nodeIDs, uint64(customValidatorWeight)) } -func loadCostonValidators() []Validator { +func loadCostonValidators(timestamp time.Time) []Validator { + if !timestamp.Before(costonValidatorsExpTime) { + return nil + } nodeIDs := []string{ "NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc", "NodeID-EkH8wyEshzEQBToAdR7Fexxcj9rrmEEHZ", @@ -98,7 +146,10 @@ func loadCostonValidators() []Validator { return createValidators(nodeIDs, uint64(costonValidatorWeight)) } -func loadSongbirdValidators() []Validator { +func loadSongbirdValidators(timestamp time.Time) []Validator { + if !timestamp.Before(songbirdValidatorsExpTime) { + return nil + } nodeIDs := []string{ "NodeID-3M9KVT6ixi4gVMisbm5TnPXYXgFN5LHuv", "NodeID-NnX4fajAmyvpL9RLfheNdc47FKKDuQW8i", diff --git a/avalanchego/snow/validators/custom_test.go b/avalanchego/snow/validators/custom_test.go new file mode 100644 index 00000000..0b2b1370 --- /dev/null +++ b/avalanchego/snow/validators/custom_test.go @@ -0,0 +1,32 @@ +package validators + +import ( + "testing" + "time" + + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/stretchr/testify/require" +) + +func TestValidatorsBeforeExpiration(t *testing.T) { + songbirdValidatorsExpTime = time.Date(2024, time.February, 1, 0, 0, 0, 0, time.UTC) + + vs := defaultValidatorSet{} + vs.initialize(constants.SongbirdID, time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)) + + vds := vs.list() + require.Len(t, vds, 20) + + expVdrs := vs.expiredValidators(constants.SongbirdID, time.Date(2024, time.February, 2, 0, 0, 0, 0, time.UTC)) + require.Len(t, expVdrs, 20) +} + +func TestValidatorsAfterExpiration(t *testing.T) { + songbirdValidatorsExpTime = time.Date(2024, time.February, 1, 0, 0, 0, 0, time.UTC) + + vs := defaultValidatorSet{} + vs.initialize(constants.SongbirdID, time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC)) + + vds := vs.list() + require.Len(t, vds, 0) +} diff --git a/avalanchego/snow/validators/manager.go b/avalanchego/snow/validators/manager.go index 0362ad2b..45e91006 100644 --- a/avalanchego/snow/validators/manager.go +++ b/avalanchego/snow/validators/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators diff --git a/avalanchego/snow/validators/set.go b/avalanchego/snow/validators/set.go index e52597d8..49738708 100644 --- a/avalanchego/snow/validators/set.go +++ b/avalanchego/snow/validators/set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators @@ -361,6 +361,7 @@ func (s *set) remove(vdrID ids.NodeID) error { // Remove i delete(s.vdrMap, vdrID) + s.vdrSlice[e] = nil s.vdrSlice = s.vdrSlice[:e] s.vdrWeights = s.vdrWeights[:e] s.vdrMaskedWeights = s.vdrMaskedWeights[:e] diff --git a/avalanchego/snow/validators/set_test.go b/avalanchego/snow/validators/set_test.go index 376da704..4c8fbd81 100644 --- a/avalanchego/snow/validators/set_test.go +++ b/avalanchego/snow/validators/set_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators @@ -6,10 +6,12 @@ package validators import ( "math" "testing" + "time" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" ) func TestSetSet(t *testing.T) { @@ -416,3 +418,36 @@ func TestSetValidatorSetCallback(t *testing.T) { require.NoError(t, err) require.Equal(t, 4, callcount) } + +func TestDefaultValidatorExpiration(t *testing.T) { + set := NewSet() + + t.Setenv("CUSTOM_VALIDATORS", "NodeID-5dDZXn99LCkDoEi6t9gTitZuQmhokxQTc,NodeID-AQghDJTU3zuQj73itPtfTZz6CxsTQVD3R") + t.Setenv("CUSTOM_VALIDATORS_EXPIRATION", "2024-02-01T00:00:00Z") + + vs := defaultValidatorSet{} + vs.initialize(constants.LocalID, time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)) + + require.Len(t, vs.list(), 2) + + for _, v := range vs.list() { + require.NoError(t, set.AddWeight(v.ID(), v.Weight())) + } + + require.Equal(t, 2, set.Len()) + + // Get expired validators + expVdrs := vs.expiredValidators(constants.LocalID, time.Date(2024, time.February, 2, 0, 0, 0, 0, time.UTC)) + require.Len(t, expVdrs, 2) + + // Remove expired validators + for _, v := range expVdrs { + require.NoError(t, set.RemoveWeight(v.ID(), v.Weight())) + } + require.Equal(t, 0, set.Len()) + + // Removing expired validators again should not error + for _, v := range expVdrs { + require.NoError(t, set.RemoveWeight(v.ID(), v.Weight())) + } +} diff --git a/avalanchego/snow/validators/state.go b/avalanchego/snow/validators/state.go index b460db4d..52c12cf1 100644 --- a/avalanchego/snow/validators/state.go +++ b/avalanchego/snow/validators/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators diff --git a/avalanchego/snow/validators/test_state.go b/avalanchego/snow/validators/test_state.go index 689cd985..d070f10c 100644 --- a/avalanchego/snow/validators/test_state.go +++ b/avalanchego/snow/validators/test_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators diff --git a/avalanchego/snow/validators/validator.go b/avalanchego/snow/validators/validator.go index 2d4a41b8..bfee1ad9 100644 --- a/avalanchego/snow/validators/validator.go +++ b/avalanchego/snow/validators/validator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators diff --git a/avalanchego/staking/tls.go b/avalanchego/staking/tls.go index b27d0c97..6612d544 100644 --- a/avalanchego/staking/tls.go +++ b/avalanchego/staking/tls.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package staking diff --git a/avalanchego/staking/tls_test.go b/avalanchego/staking/tls_test.go index de774d38..8e49ccf8 100644 --- a/avalanchego/staking/tls_test.go +++ b/avalanchego/staking/tls_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package staking diff --git a/avalanchego/tests/e2e/banff/suites.go b/avalanchego/tests/e2e/banff/suites.go new file mode 100644 index 00000000..c6c6e544 --- /dev/null +++ b/avalanchego/tests/e2e/banff/suites.go @@ -0,0 +1,147 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// Implements tests for the banff network upgrade. +package banff + +import ( + "context" + + ginkgo "github.com/onsi/ginkgo/v2" + + "github.com/onsi/gomega" + + "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/tests" + "github.com/ava-labs/avalanchego/tests/e2e" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" +) + +var _ = ginkgo.Describe("[Banff]", func() { + ginkgo.It("can send custom assets X->P and P->X", + // use this for filtering tests by labels + // ref. https://onsi.github.io/ginkgo/#spec-labels + ginkgo.Label( + "require-network-runner", + "xp", + "banff", + ), + func() { + ginkgo.By("reload initial snapshot for test independence", func() { + err := e2e.Env.RestoreInitialState(true /*switchOffNetworkFirst*/) + gomega.Expect(err).Should(gomega.BeNil()) + }) + + uris := e2e.Env.GetURIs() + gomega.Expect(uris).ShouldNot(gomega.BeEmpty()) + + kc := secp256k1fx.NewKeychain(genesis.EWOQKey) + var wallet primary.Wallet + ginkgo.By("initialize wallet", func() { + walletURI := uris[0] + + // 5-second is enough to fetch initial UTXOs for test cluster in "primary.NewWallet" + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + var err error + wallet, err = primary.NewWalletFromURI(ctx, walletURI, kc) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + tests.Outf("{{green}}created wallet{{/}}\n") + }) + + // Get the P-chain and the X-chain wallets + pWallet := wallet.P() + xWallet := wallet.X() + + // Pull out useful constants to use when issuing transactions. + xChainID := xWallet.BlockchainID() + owner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + genesis.EWOQKey.PublicKey().Address(), + }, + } + + var assetID ids.ID + ginkgo.By("create new X-chain asset", func() { + var err error + assetID, err = xWallet.IssueCreateAssetTx( + "RnM", + "RNM", + 9, + map[uint32][]verify.State{ + 0: { + &secp256k1fx.TransferOutput{ + Amt: 100 * units.Schmeckle, + OutputOwners: *owner, + }, + }, + }, + ) + gomega.Expect(err).Should(gomega.BeNil()) + + tests.Outf("{{green}}created new X-chain asset{{/}}: %s\n", assetID) + }) + + ginkgo.By("export new X-chain asset to P-chain", func() { + txID, err := xWallet.IssueExportTx( + constants.PlatformChainID, + []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 100 * units.Schmeckle, + OutputOwners: *owner, + }, + }, + }, + ) + gomega.Expect(err).Should(gomega.BeNil()) + + tests.Outf("{{green}}issued X-chain export{{/}}: %s\n", txID) + }) + + ginkgo.By("import new asset from X-chain on the P-chain", func() { + txID, err := pWallet.IssueImportTx(xChainID, owner) + gomega.Expect(err).Should(gomega.BeNil()) + + tests.Outf("{{green}}issued P-chain import{{/}}: %s\n", txID) + }) + + ginkgo.By("export asset from P-chain to the X-chain", func() { + txID, err := pWallet.IssueExportTx( + xChainID, + []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 100 * units.Schmeckle, + OutputOwners: *owner, + }, + }, + }, + ) + gomega.Expect(err).Should(gomega.BeNil()) + + tests.Outf("{{green}}issued P-chain export{{/}}: %s\n", txID) + }) + + ginkgo.By("import asset from P-chain on the X-chain", func() { + txID, err := xWallet.IssueImportTx(constants.PlatformChainID, owner) + gomega.Expect(err).Should(gomega.BeNil()) + + tests.Outf("{{green}}issued X-chain import{{/}}: %s\n", txID) + }) + }) +}) diff --git a/avalanchego/tests/e2e/blueberry/suites.go b/avalanchego/tests/e2e/blueberry/suites.go deleted file mode 100644 index 28b42492..00000000 --- a/avalanchego/tests/e2e/blueberry/suites.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -// Implements tests for the blueberry network upgrade. -package blueberry - -import ( - "context" - - "github.com/onsi/gomega" - - ginkgo "github.com/onsi/ginkgo/v2" - - "github.com/ava-labs/avalanchego/genesis" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/tests" - "github.com/ava-labs/avalanchego/tests/e2e" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/vms/components/avax" - "github.com/ava-labs/avalanchego/vms/components/verify" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/ava-labs/avalanchego/wallet/subnet/primary" -) - -var _ = e2e.DescribeLocal("[Blueberry]", func() { - ginkgo.It("can send custom assets X->P and P->X", func() { - if e2e.GetEnableWhitelistTxTests() { - ginkgo.Skip("whitelist vtx tests are enabled; skipping") - } - - uris := e2e.GetURIs() - gomega.Expect(uris).ShouldNot(gomega.BeEmpty()) - - kc := secp256k1fx.NewKeychain(genesis.EWOQKey) - var wallet primary.Wallet - ginkgo.By("initialize wallet", func() { - walletURI := uris[0] - - // 5-second is enough to fetch initial UTXOs for test cluster in "primary.NewWallet" - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) - var err error - wallet, err = primary.NewWalletFromURI(ctx, walletURI, kc) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - - tests.Outf("{{green}}created wallet{{/}}\n") - }) - - // Get the P-chain and the X-chain wallets - pWallet := wallet.P() - xWallet := wallet.X() - - // Pull out useful constants to use when issuing transactions. - xChainID := xWallet.BlockchainID() - owner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{ - genesis.EWOQKey.PublicKey().Address(), - }, - } - - var assetID ids.ID - ginkgo.By("create new X-chain asset", func() { - var err error - assetID, err = xWallet.IssueCreateAssetTx( - "RnM", - "RNM", - 9, - map[uint32][]verify.State{ - 0: { - &secp256k1fx.TransferOutput{ - Amt: 100 * units.Schmeckle, - OutputOwners: *owner, - }, - }, - }, - ) - gomega.Expect(err).Should(gomega.BeNil()) - - tests.Outf("{{green}}created new X-chain asset{{/}}: %s\n", assetID) - }) - - ginkgo.By("export new X-chain asset to P-chain", func() { - txID, err := xWallet.IssueExportTx( - constants.PlatformChainID, - []*avax.TransferableOutput{ - { - Asset: avax.Asset{ - ID: assetID, - }, - Out: &secp256k1fx.TransferOutput{ - Amt: 100 * units.Schmeckle, - OutputOwners: *owner, - }, - }, - }, - ) - gomega.Expect(err).Should(gomega.BeNil()) - - tests.Outf("{{green}}issued X-chain export{{/}}: %s\n", txID) - }) - - ginkgo.By("import new asset from X-chain on the P-chain", func() { - txID, err := pWallet.IssueImportTx(xChainID, owner) - gomega.Expect(err).Should(gomega.BeNil()) - - tests.Outf("{{green}}issued P-chain import{{/}}: %s\n", txID) - }) - - ginkgo.By("export asset from P-chain to the X-chain", func() { - txID, err := pWallet.IssueExportTx( - xChainID, - []*avax.TransferableOutput{ - { - Asset: avax.Asset{ - ID: assetID, - }, - Out: &secp256k1fx.TransferOutput{ - Amt: 100 * units.Schmeckle, - OutputOwners: *owner, - }, - }, - }, - ) - gomega.Expect(err).Should(gomega.BeNil()) - - tests.Outf("{{green}}issued P-chain export{{/}}: %s\n", txID) - }) - - ginkgo.By("import asset from P-chain on the X-chain", func() { - txID, err := xWallet.IssueImportTx(constants.PlatformChainID, owner) - gomega.Expect(err).Should(gomega.BeNil()) - - tests.Outf("{{green}}issued X-chain import{{/}}: %s\n", txID) - }) - }) -}) diff --git a/avalanchego/tests/e2e/describe.go b/avalanchego/tests/e2e/describe.go index d648873e..8e9bf861 100644 --- a/avalanchego/tests/e2e/describe.go +++ b/avalanchego/tests/e2e/describe.go @@ -7,12 +7,6 @@ import ( ginkgo "github.com/onsi/ginkgo/v2" ) -// DescribeLocal annotates the tests that requires local network-runner. -// Can only run with local cluster. -func DescribeLocal(text string, body func()) bool { - return ginkgo.Describe("[Local] "+text, body) -} - // DescribeXChain annotates the tests for X-Chain. // Can run with any type of cluster (e.g., local, fuji, mainnet). func DescribeXChain(text string, body func()) bool { diff --git a/avalanchego/tests/e2e/e2e.go b/avalanchego/tests/e2e/e2e.go index 9df8735a..3328475a 100644 --- a/avalanchego/tests/e2e/e2e.go +++ b/avalanchego/tests/e2e/e2e.go @@ -5,109 +5,256 @@ package e2e import ( + "context" + "fmt" + "os" + "strings" "sync" "time" + "github.com/onsi/gomega" + runner_sdk "github.com/ava-labs/avalanche-network-runner-sdk" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) +type ClusterType byte + const ( + Unknown ClusterType = iota + StandAlone + PreExisting + // Enough for primary.NewWallet to fetch initial UTXOs. DefaultWalletCreationTimeout = 5 * time.Second // Defines default tx confirmation timeout. // Enough for test/custom networks. - DefaultConfirmTxTimeout = 10 * time.Second + DefaultConfirmTxTimeout = 20 * time.Second + + DefaultShutdownTimeout = 2 * time.Minute ) -var ( +// Env is the global struct containing all we need to test +var Env = &TestEnvinronment{ + testEnvironmentConfig: &testEnvironmentConfig{ + clusterType: Unknown, + }, +} + +type testEnvironmentConfig struct { + clusterType ClusterType + logLevel string + networkRunnerGRPCEndpoint string + avalancheGoExecPath string + avalancheGoLogLevel string + testKeysFile string + + // we snapshot initial state, right after starting cluster + // to be able to reset state if needed and isolate tests + snapshotName string +} + +type TestEnvinronment struct { + *testEnvironmentConfig + runnerMu sync.RWMutex runnerCli runner_sdk.Client runnerGRPCEp string -) -func SetRunnerClient(logLevel string, gRPCEp string) (cli runner_sdk.Client, err error) { - runnerMu.Lock() - defer runnerMu.Unlock() + urisMu sync.RWMutex + uris []string + + testKeysMu sync.RWMutex + testKeys []*crypto.PrivateKeySECP256K1R + + snapMu sync.RWMutex + snapped bool +} + +// should be called only once +// must be called before StartCluster +// Note that either networkRunnerGRPCEp or uris must be specified +func (te *TestEnvinronment) ConfigCluster( + logLevel string, + networkRunnerGRPCEp string, + avalancheGoExecPath string, + avalancheGoLogLevel string, + uris string, + testKeysFile string, +) error { + if avalancheGoExecPath != "" { + if _, err := os.Stat(avalancheGoExecPath); err != nil { + return fmt.Errorf("could not find avalanchego binary: %w", err) + } + } - cli, err = runner_sdk.New(runner_sdk.Config{ + te.testKeysFile = testKeysFile + te.snapshotName = "ginkgo" + time.Now().String() + switch { + case networkRunnerGRPCEp != "" && len(uris) == 0: + te.clusterType = StandAlone + te.logLevel = logLevel + te.networkRunnerGRPCEndpoint = networkRunnerGRPCEp + te.avalancheGoExecPath = avalancheGoExecPath + te.avalancheGoLogLevel = avalancheGoLogLevel + + err := te.setRunnerClient(te.logLevel, te.networkRunnerGRPCEndpoint) + if err != nil { + return fmt.Errorf("could not setup network-runner client: %w", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + presp, err := te.GetRunnerClient().Ping(ctx) + cancel() + if err != nil { + return fmt.Errorf("could not ping network-runner client: %w", err) + } + tests.Outf("{{green}}network-runner running in PID %d{{/}}\n", presp.Pid) + + // URIs will be set upon cluster start here + return nil + + case networkRunnerGRPCEp == "" && len(uris) != 0: + te.clusterType = PreExisting + uriSlice := strings.Split(uris, ",") + te.setURIs(uriSlice) + tests.Outf("{{green}}URIs:{{/}} %q\n", uriSlice) + return nil + + default: + return fmt.Errorf("either network-runner-grpc-endpoint or uris should be specified, not both") + } +} + +func (te *TestEnvinronment) LoadKeys() error { + // load test keys + if len(te.testKeysFile) == 0 { + return fmt.Errorf("test keys file not provided") + } + testKeys, err := tests.LoadHexTestKeys(te.testKeysFile) + if err != nil { + return fmt.Errorf("failed loading test keys: %w", err) + } + te.setTestKeys(testKeys) + return nil +} + +func (te *TestEnvinronment) StartCluster() error { + switch te.clusterType { + case StandAlone: + tests.Outf("{{magenta}}starting network-runner with %q{{/}}\n", te.avalancheGoExecPath) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + resp, err := te.GetRunnerClient().Start(ctx, te.avalancheGoExecPath, + runner_sdk.WithNumNodes(5), + runner_sdk.WithGlobalNodeConfig(fmt.Sprintf(`{"log-level":"%s"}`, te.avalancheGoLogLevel)), + ) + cancel() + if err != nil { + return fmt.Errorf("could not start network-runner: %w", err) + } + tests.Outf("{{green}}successfully started network-runner: {{/}} %+v\n", resp.ClusterInfo.NodeNames) + + // start is async, so wait some time for cluster health + time.Sleep(time.Minute) + + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) + _, err = te.GetRunnerClient().Health(ctx) + cancel() + if err != nil { + return fmt.Errorf("could not check health network-runner: %w", err) + } + + return te.refreshURIs() + + case PreExisting: + return nil // nothing to do, really + + default: + return fmt.Errorf("unhandled cluster type") + } +} + +func (te *TestEnvinronment) refreshURIs() error { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + uriSlice, err := te.GetRunnerClient().URIs(ctx) + cancel() + if err != nil { + return fmt.Errorf("could not retrieve URIs: %w", err) + } + te.setURIs(uriSlice) + tests.Outf("{{green}}URIs:{{/}} %q\n", uriSlice) + return nil +} + +func (te *TestEnvinronment) setRunnerClient(logLevel string, gRPCEp string) error { + te.runnerMu.Lock() + defer te.runnerMu.Unlock() + + cli, err := runner_sdk.New(runner_sdk.Config{ LogLevel: logLevel, Endpoint: gRPCEp, DialTimeout: 10 * time.Second, }) if err != nil { - return nil, err + return err } - if runnerCli != nil { - runnerCli.Close() + if te.runnerCli != nil { + te.runnerCli.Close() } - runnerCli = cli - runnerGRPCEp = gRPCEp - return cli, err + te.runnerCli = cli + te.runnerGRPCEp = gRPCEp + return err } -func GetRunnerClient() (cli runner_sdk.Client) { - runnerMu.RLock() - cli = runnerCli - runnerMu.RUnlock() +func (te *TestEnvinronment) GetRunnerClient() (cli runner_sdk.Client) { + te.runnerMu.RLock() + cli = te.runnerCli + te.runnerMu.RUnlock() return cli } -func CloseRunnerClient() (err error) { - runnerMu.Lock() - err = runnerCli.Close() - runnerMu.Unlock() +func (te *TestEnvinronment) closeRunnerClient() (err error) { + te.runnerMu.Lock() + err = te.runnerCli.Close() + te.runnerMu.Unlock() return err } -func GetRunnerGRPCEndpoint() (ep string) { - runnerMu.RLock() - ep = runnerGRPCEp - runnerMu.RUnlock() +func (te *TestEnvinronment) GetRunnerGRPCEndpoint() (ep string) { + te.runnerMu.RLock() + ep = te.runnerGRPCEp + te.runnerMu.RUnlock() return ep } -var ( - urisMu sync.RWMutex - uris []string -) - -func SetURIs(us []string) { - urisMu.Lock() - uris = us - urisMu.Unlock() +func (te *TestEnvinronment) setURIs(us []string) { + te.urisMu.Lock() + te.uris = us + te.urisMu.Unlock() } -func GetURIs() []string { - urisMu.RLock() - us := uris - urisMu.RUnlock() +func (te *TestEnvinronment) GetURIs() []string { + te.urisMu.RLock() + us := te.uris + te.urisMu.RUnlock() return us } -var ( - testKeysMu sync.RWMutex - testKeys []*crypto.PrivateKeySECP256K1R -) - -func SetTestKeys(ks []*crypto.PrivateKeySECP256K1R) { - testKeysMu.Lock() - testKeys = ks - testKeysMu.Unlock() -} - -func GetTestKeys() []*crypto.PrivateKeySECP256K1R { - testKeysMu.RLock() - ks := testKeys - testKeysMu.RUnlock() - return ks +func (te *TestEnvinronment) setTestKeys(ks []*crypto.PrivateKeySECP256K1R) { + te.testKeysMu.Lock() + te.testKeys = ks + te.testKeysMu.Unlock() } -func LoadTestKeys() ([]*crypto.PrivateKeySECP256K1R, []ids.ShortID, *secp256k1fx.Keychain) { - testKeys := GetTestKeys() +func (te *TestEnvinronment) GetTestKeys() ([]*crypto.PrivateKeySECP256K1R, []ids.ShortID, *secp256k1fx.Keychain) { + te.testKeysMu.RLock() + testKeys := te.testKeys + te.testKeysMu.RUnlock() testKeyAddrs := make([]ids.ShortID, len(testKeys)) for i := range testKeyAddrs { testKeyAddrs[i] = testKeys[i].PublicKey().Address() @@ -116,20 +263,73 @@ func LoadTestKeys() ([]*crypto.PrivateKeySECP256K1R, []ids.ShortID, *secp256k1fx return testKeys, testKeyAddrs, keyChain } -var ( - enableWhitelistTxTestMu sync.RWMutex - enableWhitelistTxTests bool -) +func (te *TestEnvinronment) ShutdownCluster() error { + if te.GetRunnerGRPCEndpoint() == "" { + // we connected directly to existing cluster + // nothing to shutdown + return nil + } + + runnerCli := te.GetRunnerClient() + if runnerCli == nil { + return fmt.Errorf("not network-runner cli") + } + + tests.Outf("{{red}}shutting down network-runner cluster{{/}}\n") + ctx, cancel := context.WithTimeout(context.Background(), DefaultShutdownTimeout) + _, err := runnerCli.Stop(ctx) + cancel() + if err != nil { + return err + } + + tests.Outf("{{red}}shutting down network-runner client{{/}}\n") + return te.closeRunnerClient() +} + +func (te *TestEnvinronment) SnapInitialState() error { + te.snapMu.RLock() + defer te.snapMu.RUnlock() -func SetEnableWhitelistTxTests(b bool) { - enableWhitelistTxTestMu.Lock() - enableWhitelistTxTests = b - enableWhitelistTxTestMu.Unlock() + if te.snapped { + return nil // initial state snapshot already captured + } + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + _, err := te.runnerCli.SaveSnapshot(ctx, te.snapshotName) + cancel() + if err != nil { + return err + } + te.snapped = true + return nil } -func GetEnableWhitelistTxTests() (b bool) { - enableWhitelistTxTestMu.RLock() - b = enableWhitelistTxTests - enableWhitelistTxTestMu.RUnlock() - return b +func (te *TestEnvinronment) RestoreInitialState(switchOffNetworkFirst bool) error { + te.snapMu.Lock() + defer te.snapMu.Unlock() + + if switchOffNetworkFirst { + ctx, cancel := context.WithTimeout(context.Background(), DefaultShutdownTimeout) + _, err := te.GetRunnerClient().Stop(ctx) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + } + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + _, err := te.GetRunnerClient().LoadSnapshot(ctx, te.snapshotName) + cancel() + if err != nil { + return err + } + + // make sure cluster goes back to health before moving on + ctx, cancel = context.WithTimeout(context.Background(), DefaultShutdownTimeout) + _, err = te.GetRunnerClient().Health(ctx) + cancel() + if err != nil { + return fmt.Errorf("could not check health network-runner: %w", err) + } + + return te.refreshURIs() } diff --git a/avalanchego/tests/e2e/e2e_test.go b/avalanchego/tests/e2e/e2e_test.go index 470c487b..00f501d5 100644 --- a/avalanchego/tests/e2e/e2e_test.go +++ b/avalanchego/tests/e2e/e2e_test.go @@ -4,27 +4,22 @@ package e2e_test import ( - "context" "flag" - "fmt" - "os" - "strings" "testing" - "time" - - runner_sdk "github.com/ava-labs/avalanche-network-runner-sdk" - "github.com/ava-labs/avalanchego/tests" - "github.com/ava-labs/avalanchego/tests/e2e" ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + "github.com/ava-labs/avalanchego/tests/e2e" + // ensure test packages are scanned by ginkgo - _ "github.com/ava-labs/avalanchego/tests/e2e/blueberry" + _ "github.com/ava-labs/avalanchego/tests/e2e/banff" + _ "github.com/ava-labs/avalanchego/tests/e2e/p" _ "github.com/ava-labs/avalanchego/tests/e2e/ping" _ "github.com/ava-labs/avalanchego/tests/e2e/static-handlers" - _ "github.com/ava-labs/avalanchego/tests/e2e/whitelist-vtx" _ "github.com/ava-labs/avalanchego/tests/e2e/x/transfer" + _ "github.com/ava-labs/avalanchego/tests/e2e/x/whitelist-vtx" ) func TestE2E(t *testing.T) { @@ -33,6 +28,7 @@ func TestE2E(t *testing.T) { } var ( + // helpers to parse test flags logLevel string networkRunnerGRPCEp string @@ -41,12 +37,9 @@ var ( uris string - testKeysFile string - enableWhitelistTxTests bool + testKeysFile string ) -// TODO: support existing keys - func init() { flag.StringVar( &logLevel, @@ -89,91 +82,37 @@ func init() { "", "file that contains a list of new-line separated hex-encoded secp256k1 private keys (assume test keys are pre-funded, for test networks)", ) - - flag.BoolVar( - &enableWhitelistTxTests, - "enable-whitelist-vtx-tests", - false, - "true to enable whitelist vtx tests", - ) } var _ = ginkgo.BeforeSuite(func() { - e2e.SetEnableWhitelistTxTests(enableWhitelistTxTests) - - if networkRunnerAvalancheGoExecPath != "" { - _, err := os.Stat(networkRunnerAvalancheGoExecPath) - gomega.Expect(err).Should(gomega.BeNil()) - } - - // run with local network-runner - if networkRunnerGRPCEp != "" { - gomega.Expect(uris).Should(gomega.BeEmpty()) - - runnerCli, err := e2e.SetRunnerClient(logLevel, networkRunnerGRPCEp) - gomega.Expect(err).Should(gomega.BeNil()) - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - presp, err := runnerCli.Ping(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - tests.Outf("{{green}}network-runner running in PID %d{{/}}\n", presp.Pid) - - tests.Outf("{{magenta}}starting network-runner with %q{{/}}\n", networkRunnerAvalancheGoExecPath) - ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) - resp, err := runnerCli.Start(ctx, networkRunnerAvalancheGoExecPath, - runner_sdk.WithNumNodes(5), - runner_sdk.WithGlobalNodeConfig(fmt.Sprintf(`{"log-level":"%s"}`, networkRunnerAvalancheGoLogLevel)), - ) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - tests.Outf("{{green}}successfully started network-runner :{{/}} %+v\n", resp.ClusterInfo.NodeNames) - - // start is async, so wait some time for cluster health - time.Sleep(time.Minute) - - ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) - _, err = runnerCli.Health(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - - var uriSlice []string - ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) - uriSlice, err = runnerCli.URIs(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - e2e.SetURIs(uriSlice) - } - - // connect directly to existing cluster - if uris != "" { - gomega.Expect(networkRunnerGRPCEp).Should(gomega.BeEmpty()) - - uriSlice := strings.Split(uris, ",") - e2e.SetURIs(uriSlice) - } - uriSlice := e2e.GetURIs() - tests.Outf("{{green}}URIs:{{/}} %q\n", uriSlice) - - gomega.Expect(testKeysFile).ShouldNot(gomega.BeEmpty()) - testKeys, err := tests.LoadHexTestKeys(testKeysFile) + err := e2e.Env.ConfigCluster( + logLevel, + networkRunnerGRPCEp, + networkRunnerAvalancheGoExecPath, + networkRunnerAvalancheGoLogLevel, + uris, + testKeysFile, + ) + gomega.Expect(err).Should(gomega.BeNil()) + + // check cluster can be started + err = e2e.Env.StartCluster() + gomega.Expect(err).Should(gomega.BeNil()) + + // load keys + err = e2e.Env.LoadKeys() + gomega.Expect(err).Should(gomega.BeNil()) + + // take initial snapshot. cluster will be switched off + err = e2e.Env.SnapInitialState() + gomega.Expect(err).Should(gomega.BeNil()) + + // restart cluster + err = e2e.Env.RestoreInitialState(false /*switchOffNetworkFirst*/) gomega.Expect(err).Should(gomega.BeNil()) - e2e.SetTestKeys(testKeys) }) var _ = ginkgo.AfterSuite(func() { - if networkRunnerGRPCEp != "" { - runnerCli := e2e.GetRunnerClient() - gomega.Expect(runnerCli).ShouldNot(gomega.BeNil()) - - tests.Outf("{{red}}shutting down network-runner cluster{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := runnerCli.Stop(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - - tests.Outf("{{red}}shutting down network-runner client{{/}}\n") - err = e2e.CloseRunnerClient() - gomega.Expect(err).Should(gomega.BeNil()) - } + err := e2e.Env.ShutdownCluster() + gomega.Expect(err).Should(gomega.BeNil()) }) diff --git a/avalanchego/tests/e2e/p/permissionless_subnets.go b/avalanchego/tests/e2e/p/permissionless_subnets.go new file mode 100644 index 00000000..c928e8d7 --- /dev/null +++ b/avalanchego/tests/e2e/p/permissionless_subnets.go @@ -0,0 +1,249 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package p + +import ( + "context" + "fmt" + "time" + + ginkgo "github.com/onsi/ginkgo/v2" + + "github.com/onsi/gomega" + + "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/tests" + "github.com/ava-labs/avalanchego/tests/e2e" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/avm" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/status" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" +) + +var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { + ginkgo.It("subnets operations", + // use this for filtering tests by labels + // ref. https://onsi.github.io/ginkgo/#spec-labels + ginkgo.Label( + "require-network-runner", + "xp", + "permissionless-subnets", + ), + func() { + ginkgo.By("reload initial snapshot for test independence", func() { + err := e2e.Env.RestoreInitialState(true /*switchOffNetworkFirst*/) + gomega.Expect(err).Should(gomega.BeNil()) + }) + + rpcEps := e2e.Env.GetURIs() + gomega.Expect(rpcEps).ShouldNot(gomega.BeEmpty()) + nodeURI := rpcEps[0] + + tests.Outf("{{blue}} setting up keys {{/}}\n") + testKey := genesis.EWOQKey + keyChain := secp256k1fx.NewKeychain(testKey) + + var baseWallet primary.Wallet + ginkgo.By("setup wallet", func() { + var err error + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + baseWallet, err = primary.NewWalletFromURI(ctx, nodeURI, keyChain) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + }) + + pWallet := baseWallet.P() + pChainClient := platformvm.NewClient(nodeURI) + xWallet := baseWallet.X() + xChainClient := avm.NewClient(nodeURI, xWallet.BlockchainID().String()) + xChainID := xWallet.BlockchainID() + + owner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + testKey.PublicKey().Address(), + }, + } + + var subnetID ids.ID + ginkgo.By("create a permissioned subnet", func() { + var err error + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + subnetID, err = pWallet.IssueCreateSubnetTx( + owner, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(subnetID, err).Should(gomega.Not(gomega.Equal(constants.PrimaryNetworkID))) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, subnetID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + + var subnetAssetID ids.ID + ginkgo.By("create a custom asset for the permissionless subnet", func() { + var err error + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + subnetAssetID, err = xWallet.IssueCreateAssetTx( + "RnM", + "RNM", + 9, + map[uint32][]verify.State{ + 0: { + &secp256k1fx.TransferOutput{ + Amt: 100 * units.MegaAvax, + OutputOwners: *owner, + }, + }, + }, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := xChainClient.GetTxStatus(ctx, subnetAssetID) + cancel() + gomega.Expect(txStatus, err).To(gomega.Equal(choices.Accepted)) + }) + + ginkgo.By(fmt.Sprintf("Send 100 MegaAvax of asset %s to the P-chain", subnetAssetID), func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + exportTxID, err := xWallet.IssueExportTx( + constants.PlatformChainID, + []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: subnetAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 100 * units.MegaAvax, + OutputOwners: *owner, + }, + }, + }, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := xChainClient.GetTxStatus(ctx, exportTxID) + cancel() + gomega.Expect(txStatus, err).To(gomega.Equal(choices.Accepted)) + }) + + ginkgo.By(fmt.Sprintf("Import the 100 MegaAvax of asset %s from the X-chain into the P-chain", subnetAssetID), func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + importTxID, err := pWallet.IssueImportTx( + xChainID, + owner, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, importTxID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + + ginkgo.By("make subnet permissionless", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + transformSubnetTxID, err := pWallet.IssueTransformSubnetTx( + subnetID, + subnetAssetID, + 50*units.MegaAvax, + 100*units.MegaAvax, + reward.PercentDenominator, + reward.PercentDenominator, + 1, + 100*units.MegaAvax, + time.Second, + 365*24*time.Hour, + 0, + 1, + 5, + .80*reward.PercentDenominator, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, transformSubnetTxID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + + validatorStartTime := time.Now().Add(time.Minute) + ginkgo.By("add permissionless validator", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + addSubnetValidatorTxID, err := pWallet.IssueAddPermissionlessValidatorTx( + &validator.SubnetValidator{ + Validator: validator.Validator{ + NodeID: genesis.LocalConfig.InitialStakers[0].NodeID, + Start: uint64(validatorStartTime.Unix()), + End: uint64(validatorStartTime.Add(5 * time.Second).Unix()), + Wght: 25 * units.MegaAvax, + }, + Subnet: subnetID, + }, + &signer.Empty{}, + subnetAssetID, + &secp256k1fx.OutputOwners{}, + &secp256k1fx.OutputOwners{}, + reward.PercentDenominator, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, addSubnetValidatorTxID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + + delegatorStartTime := validatorStartTime + ginkgo.By("add permissionless delegator", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + addSubnetDelegatorTxID, err := pWallet.IssueAddPermissionlessDelegatorTx( + &validator.SubnetValidator{ + Validator: validator.Validator{ + NodeID: genesis.LocalConfig.InitialStakers[0].NodeID, + Start: uint64(delegatorStartTime.Unix()), + End: uint64(delegatorStartTime.Add(5 * time.Second).Unix()), + Wght: 25 * units.MegaAvax, + }, + Subnet: subnetID, + }, + subnetAssetID, + &secp256k1fx.OutputOwners{}, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, addSubnetDelegatorTxID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + }) +}) diff --git a/avalanchego/tests/e2e/p/workflow.go b/avalanchego/tests/e2e/p/workflow.go new file mode 100644 index 00000000..f4b40691 --- /dev/null +++ b/avalanchego/tests/e2e/p/workflow.go @@ -0,0 +1,235 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package p + +import ( + "context" + "errors" + "fmt" + "time" + + ginkgo "github.com/onsi/ginkgo/v2" + + "github.com/onsi/gomega" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/tests" + "github.com/ava-labs/avalanchego/tests/e2e" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/avm" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/avalanchego/vms/platformvm/status" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" +) + +// PChainWorkflow is an integration test for normal P-Chain operations +// - Issues an Add Validator and an Add Delegator using the funding address +// - Exports AVAX from the P-Chain funding address to the X-Chain created address +// - Exports AVAX from the X-Chain created address to the P-Chain created address +// - Checks the expected value of the funding address + +var _ = e2e.DescribePChain("[Workflow]", func() { + ginkgo.It("P-chain main operations", + // use this for filtering tests by labels + // ref. https://onsi.github.io/ginkgo/#spec-labels + ginkgo.Label( + "require-network-runner", + "xp", + "workflow", + ), + ginkgo.FlakeAttempts(2), + func() { + rpcEps := e2e.Env.GetURIs() + gomega.Expect(rpcEps).ShouldNot(gomega.BeEmpty()) + nodeURI := rpcEps[0] + + tests.Outf("{{blue}} setting up keys {{/}}\n") + _, testKeyAddrs, keyChain := e2e.Env.GetTestKeys() + + tests.Outf("{{blue}} setting up wallet {{/}}\n") + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + baseWallet, err := primary.NewWalletFromURI(ctx, nodeURI, keyChain) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + pWallet := baseWallet.P() + avaxAssetID := baseWallet.P().AVAXAssetID() + xWallet := baseWallet.X() + pChainClient := platformvm.NewClient(nodeURI) + xChainClient := avm.NewClient(nodeURI, xWallet.BlockchainID().String()) + + tests.Outf("{{blue}} fetching minimal stake amounts {{/}}\n") + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + minValStake, minDelStake, err := pChainClient.GetMinStake(ctx, constants.PlatformChainID) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + tests.Outf("{{green}} minimal validator stake: %d {{/}}\n", minValStake) + tests.Outf("{{green}} minimal delegator stake: %d {{/}}\n", minDelStake) + + tests.Outf("{{blue}} fetching tx fee {{/}}\n") + infoClient := info.NewClient(nodeURI) + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + fees, err := infoClient.GetTxFee(ctx) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + txFees := uint64(fees.TxFee) + tests.Outf("{{green}} txFee: %d {{/}}\n", txFees) + + // amount to transfer from P to X chain + toTransfer := 1 * units.Avax + + pShortAddr := testKeyAddrs[0] + xTargetAddr := testKeyAddrs[1] + ginkgo.By("check selected keys have sufficient funds", func() { + pBalances, err := pWallet.Builder().GetBalance() + pBalance := pBalances[avaxAssetID] + minBalance := minValStake + txFees + minDelStake + txFees + toTransfer + txFees + gomega.Expect(pBalance, err).To(gomega.BeNumerically(">=", minBalance)) + }) + // create validator data + validatorStartTimeDiff := 30 * time.Second + vdrStartTime := time.Now().Add(validatorStartTimeDiff) + + vdr := &validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Start: uint64(vdrStartTime.Unix()), + End: uint64(vdrStartTime.Add(72 * time.Hour).Unix()), + Wght: minValStake, + } + rewardOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{pShortAddr}, + } + shares := uint32(20000) // TODO: retrieve programmatically + + ginkgo.By("issue add validator tx", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + addValidatorTxID, err := pWallet.IssueAddValidatorTx( + vdr, + rewardOwner, + shares, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, addValidatorTxID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + + ginkgo.By("issue add delegator tx", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + addDelegatorTxID, err := pWallet.IssueAddDelegatorTx( + vdr, + rewardOwner, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, addDelegatorTxID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + + // retrieve initial balances + pBalances, err := pWallet.Builder().GetBalance() + gomega.Expect(err).Should(gomega.BeNil()) + pStartBalance := pBalances[avaxAssetID] + tests.Outf("{{blue}} P-chain balance before P->X export: %d {{/}}\n", pStartBalance) + + xBalances, err := xWallet.Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + xStartBalance := xBalances[avaxAssetID] + tests.Outf("{{blue}} X-chain balance before P->X export: %d {{/}}\n", xStartBalance) + + outputOwner := secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + xTargetAddr, + }, + } + output := &secp256k1fx.TransferOutput{ + Amt: toTransfer, + OutputOwners: outputOwner, + } + + ginkgo.By("export avax from P to X chain", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + exportTxID, err := pWallet.IssueExportTx( + xWallet.BlockchainID(), + []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: output, + }, + }, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := pChainClient.GetTxStatus(ctx, exportTxID) + cancel() + gomega.Expect(txStatus.Status, err).To(gomega.Equal(status.Committed)) + }) + + // check balances post export + pBalances, err = pWallet.Builder().GetBalance() + gomega.Expect(err).Should(gomega.BeNil()) + pPreImportBalance := pBalances[avaxAssetID] + tests.Outf("{{blue}} P-chain balance after P->X export: %d {{/}}\n", pPreImportBalance) + + xBalances, err = xWallet.Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + xPreImportBalance := xBalances[avaxAssetID] + tests.Outf("{{blue}} X-chain balance after P->X export: %d {{/}}\n", xPreImportBalance) + + gomega.Expect(xPreImportBalance).To(gomega.Equal(xStartBalance)) // import not performed yet + gomega.Expect(pPreImportBalance).To(gomega.Equal(pStartBalance - toTransfer - txFees)) + + ginkgo.By("import avax from P into X chain", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + importTxID, err := xWallet.IssueImportTx( + constants.PlatformChainID, + &outputOwner, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil(), fmt.Errorf("error timeout: %v", errors.Is(err, context.DeadlineExceeded))) + + ctx, cancel = context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + txStatus, err := xChainClient.GetTxStatus(ctx, importTxID) + cancel() + gomega.Expect(txStatus, err).To(gomega.Equal(choices.Accepted)) + }) + + // check balances post import + pBalances, err = pWallet.Builder().GetBalance() + gomega.Expect(err).Should(gomega.BeNil()) + pFinalBalance := pBalances[avaxAssetID] + tests.Outf("{{blue}} P-chain balance after P->X import: %d {{/}}\n", pFinalBalance) + + xBalances, err = xWallet.Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + xFinalBalance := xBalances[avaxAssetID] + tests.Outf("{{blue}} X-chain balance after P->X import: %d {{/}}\n", xFinalBalance) + + gomega.Expect(xFinalBalance).To(gomega.Equal(xPreImportBalance + toTransfer - txFees)) // import not performed yet + gomega.Expect(pFinalBalance).To(gomega.Equal(pPreImportBalance)) + }) +}) diff --git a/avalanchego/tests/e2e/ping/suites.go b/avalanchego/tests/e2e/ping/suites.go index 2573d6d8..5f9d6256 100644 --- a/avalanchego/tests/e2e/ping/suites.go +++ b/avalanchego/tests/e2e/ping/suites.go @@ -14,18 +14,25 @@ import ( "github.com/onsi/gomega" ) -var _ = e2e.DescribeLocal("[Ping]", func() { - ginkgo.It("can ping network-runner RPC server", func() { - if e2e.GetRunnerGRPCEndpoint() == "" { - ginkgo.Skip("no local network-runner, skipping") - } +var _ = ginkgo.Describe("[Ping]", func() { + ginkgo.It("can ping network-runner RPC server", + // use this for filtering tests by labels + // ref. https://onsi.github.io/ginkgo/#spec-labels + ginkgo.Label( + "require-network-runner", + "ping", + ), + func() { + if e2e.Env.GetRunnerGRPCEndpoint() == "" { + ginkgo.Skip("no local network-runner, skipping") + } - runnerCli := e2e.GetRunnerClient() - gomega.Expect(runnerCli).ShouldNot(gomega.BeNil()) + runnerCli := e2e.Env.GetRunnerClient() + gomega.Expect(runnerCli).ShouldNot(gomega.BeNil()) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := runnerCli.Ping(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - }) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + _, err := runnerCli.Ping(ctx) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + }) }) diff --git a/avalanchego/tests/e2e/static-handlers/suites.go b/avalanchego/tests/e2e/static-handlers/suites.go index 9fedee97..61d8c9c1 100644 --- a/avalanchego/tests/e2e/static-handlers/suites.go +++ b/avalanchego/tests/e2e/static-handlers/suites.go @@ -26,93 +26,100 @@ import ( ) var _ = ginkgo.Describe("[StaticHandlers]", func() { - ginkgo.It("can make calls to avm static api", func() { - addrMap := map[string]string{} - for _, addrStr := range []string{ - "A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy", - "6mxBGnjGDCKgkVe7yfrmvMA7xE7qCv3vv", - "6ncQ19Q2U4MamkCYzshhD8XFjfwAWFzTa", - "Jz9ayEDt7dx9hDx45aXALujWmL9ZUuqe7", - } { - addr, err := ids.ShortFromString(addrStr) - gomega.Expect(err).Should(gomega.BeNil()) - addrMap[addrStr], err = address.FormatBech32(constants.NetworkIDToHRP[constants.LocalID], addr[:]) - gomega.Expect(err).Should(gomega.BeNil()) - } - avmArgs := avm.BuildGenesisArgs{ - Encoding: formatting.Hex, - GenesisData: map[string]avm.AssetDefinition{ - "asset1": { - Name: "myFixedCapAsset", - Symbol: "MFCA", - Denomination: 8, - InitialState: map[string][]interface{}{ - "fixedCap": { - avm.Holder{ - Amount: 100000, - Address: addrMap["A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy"], - }, - avm.Holder{ - Amount: 100000, - Address: addrMap["6mxBGnjGDCKgkVe7yfrmvMA7xE7qCv3vv"], - }, - avm.Holder{ - Amount: json.Uint64(50000), - Address: addrMap["6ncQ19Q2U4MamkCYzshhD8XFjfwAWFzTa"], - }, - avm.Holder{ - Amount: json.Uint64(50000), - Address: addrMap["Jz9ayEDt7dx9hDx45aXALujWmL9ZUuqe7"], + ginkgo.It("can make calls to avm static api", + // use this for filtering tests by labels + // ref. https://onsi.github.io/ginkgo/#spec-labels + ginkgo.Label( + "require-network-runner", + "static-handlers", + ), + func() { + addrMap := map[string]string{} + for _, addrStr := range []string{ + "A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy", + "6mxBGnjGDCKgkVe7yfrmvMA7xE7qCv3vv", + "6ncQ19Q2U4MamkCYzshhD8XFjfwAWFzTa", + "Jz9ayEDt7dx9hDx45aXALujWmL9ZUuqe7", + } { + addr, err := ids.ShortFromString(addrStr) + gomega.Expect(err).Should(gomega.BeNil()) + addrMap[addrStr], err = address.FormatBech32(constants.NetworkIDToHRP[constants.LocalID], addr[:]) + gomega.Expect(err).Should(gomega.BeNil()) + } + avmArgs := avm.BuildGenesisArgs{ + Encoding: formatting.Hex, + GenesisData: map[string]avm.AssetDefinition{ + "asset1": { + Name: "myFixedCapAsset", + Symbol: "MFCA", + Denomination: 8, + InitialState: map[string][]interface{}{ + "fixedCap": { + avm.Holder{ + Amount: 100000, + Address: addrMap["A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy"], + }, + avm.Holder{ + Amount: 100000, + Address: addrMap["6mxBGnjGDCKgkVe7yfrmvMA7xE7qCv3vv"], + }, + avm.Holder{ + Amount: json.Uint64(50000), + Address: addrMap["6ncQ19Q2U4MamkCYzshhD8XFjfwAWFzTa"], + }, + avm.Holder{ + Amount: json.Uint64(50000), + Address: addrMap["Jz9ayEDt7dx9hDx45aXALujWmL9ZUuqe7"], + }, }, }, }, - }, - "asset2": { - Name: "myVarCapAsset", - Symbol: "MVCA", - InitialState: map[string][]interface{}{ - "variableCap": { - avm.Owners{ - Threshold: 1, - Minters: []string{ - addrMap["A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy"], - addrMap["6mxBGnjGDCKgkVe7yfrmvMA7xE7qCv3vv"], + "asset2": { + Name: "myVarCapAsset", + Symbol: "MVCA", + InitialState: map[string][]interface{}{ + "variableCap": { + avm.Owners{ + Threshold: 1, + Minters: []string{ + addrMap["A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy"], + addrMap["6mxBGnjGDCKgkVe7yfrmvMA7xE7qCv3vv"], + }, }, - }, - avm.Owners{ - Threshold: 2, - Minters: []string{ - addrMap["6ncQ19Q2U4MamkCYzshhD8XFjfwAWFzTa"], - addrMap["Jz9ayEDt7dx9hDx45aXALujWmL9ZUuqe7"], + avm.Owners{ + Threshold: 2, + Minters: []string{ + addrMap["6ncQ19Q2U4MamkCYzshhD8XFjfwAWFzTa"], + addrMap["Jz9ayEDt7dx9hDx45aXALujWmL9ZUuqe7"], + }, }, }, }, }, - }, - "asset3": { - Name: "myOtherVarCapAsset", - InitialState: map[string][]interface{}{ - "variableCap": { - avm.Owners{ - Threshold: 1, - Minters: []string{ - addrMap["A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy"], + "asset3": { + Name: "myOtherVarCapAsset", + InitialState: map[string][]interface{}{ + "variableCap": { + avm.Owners{ + Threshold: 1, + Minters: []string{ + addrMap["A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy"], + }, }, }, }, }, }, - }, - } - uris := e2e.GetURIs() - gomega.Expect(uris).ShouldNot(gomega.BeEmpty()) - staticClient := avm.NewStaticClient(uris[0]) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := staticClient.BuildGenesis(ctx, &avmArgs) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - gomega.Expect(resp.Bytes).Should(gomega.Equal("0x0000000000030006617373657431000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6d794669786564436170417373657400044d4643410800000001000000000000000400000007000000000000c350000000000000000000000001000000013f78e510df62bc48b0829ec06d6a6b98062d695300000007000000000000c35000000000000000000000000100000001c54903de5177a16f7811771ef2f4659d9e8646710000000700000000000186a0000000000000000000000001000000013f58fda2e9ea8d9e4b181832a07b26dae286f2cb0000000700000000000186a000000000000000000000000100000001645938bb7ae2193270e6ffef009e3664d11e07c10006617373657432000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6d79566172436170417373657400044d5643410000000001000000000000000200000006000000000000000000000001000000023f58fda2e9ea8d9e4b181832a07b26dae286f2cb645938bb7ae2193270e6ffef009e3664d11e07c100000006000000000000000000000001000000023f78e510df62bc48b0829ec06d6a6b98062d6953c54903de5177a16f7811771ef2f4659d9e864671000661737365743300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000126d794f7468657256617243617041737365740000000000000100000000000000010000000600000000000000000000000100000001645938bb7ae2193270e6ffef009e3664d11e07c1279fa028")) - }) + } + uris := e2e.Env.GetURIs() + gomega.Expect(uris).ShouldNot(gomega.BeEmpty()) + staticClient := avm.NewStaticClient(uris[0]) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + resp, err := staticClient.BuildGenesis(ctx, &avmArgs) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(resp.Bytes).Should(gomega.Equal("0x0000000000030006617373657431000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6d794669786564436170417373657400044d4643410800000001000000000000000400000007000000000000c350000000000000000000000001000000013f78e510df62bc48b0829ec06d6a6b98062d695300000007000000000000c35000000000000000000000000100000001c54903de5177a16f7811771ef2f4659d9e8646710000000700000000000186a0000000000000000000000001000000013f58fda2e9ea8d9e4b181832a07b26dae286f2cb0000000700000000000186a000000000000000000000000100000001645938bb7ae2193270e6ffef009e3664d11e07c10006617373657432000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6d79566172436170417373657400044d5643410000000001000000000000000200000006000000000000000000000001000000023f58fda2e9ea8d9e4b181832a07b26dae286f2cb645938bb7ae2193270e6ffef009e3664d11e07c100000006000000000000000000000001000000023f78e510df62bc48b0829ec06d6a6b98062d6953c54903de5177a16f7811771ef2f4659d9e864671000661737365743300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000126d794f7468657256617243617041737365740000000000000100000000000000010000000600000000000000000000000100000001645938bb7ae2193270e6ffef009e3664d11e07c1279fa028")) + }) ginkgo.It("can make calls to platformvm static api", func() { keys := []*crypto.PrivateKeySECP256K1R{} @@ -143,12 +150,12 @@ var _ = ginkgo.Describe("[StaticHandlers]", func() { } } - genesisValidators := make([]api.PrimaryValidator, len(keys)) + genesisValidators := make([]api.PermissionlessValidator, len(keys)) for i, key := range keys { id := key.PublicKey().Address() addr, err := address.FormatBech32(hrp, id.Bytes()) gomega.Expect(err).Should(gomega.BeNil()) - genesisValidators[i] = api.PrimaryValidator{ + genesisValidators[i] = api.PermissionlessValidator{ Staker: api.Staker{ StartTime: json.Uint64(time.Date(1997, 1, 1, 0, 0, 0, 0, time.UTC).Unix()), EndTime: json.Uint64(time.Date(1997, 1, 30, 0, 0, 0, 0, time.UTC).Unix()), @@ -177,7 +184,7 @@ var _ = ginkgo.Describe("[StaticHandlers]", func() { Encoding: formatting.Hex, } - uris := e2e.GetURIs() + uris := e2e.Env.GetURIs() gomega.Expect(uris).ShouldNot(gomega.BeEmpty()) staticClient := api.NewStaticClient(uris[0]) diff --git a/avalanchego/tests/e2e/whitelist-vtx/suites.go b/avalanchego/tests/e2e/whitelist-vtx/suites.go deleted file mode 100644 index bff7f385..00000000 --- a/avalanchego/tests/e2e/whitelist-vtx/suites.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -// Implements X-Chain whitelist vtx (stop vertex) tests. -package whitelistvtx - -import ( - "context" - "time" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/tests" - "github.com/ava-labs/avalanchego/tests/e2e" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/vms/avm" - "github.com/ava-labs/avalanchego/vms/components/avax" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/ava-labs/avalanchego/wallet/subnet/primary" - "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" - - ginkgo "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" -) - -const ( - metricVtxIssueSuccess = "avalanche_X_whitelist_vtx_issue_success" - metricVtxIssueFailure = "avalanche_X_whitelist_vtx_issue_failure" - metricTxProcessing = "avalanche_X_whitelist_tx_processing" - metricTxAccepted = "avalanche_X_whitelist_tx_accepted_count" - metricTxRejected = "avalanche_X_whitelist_tx_rejected_count" - metricTxPollsAccepted = "avalanche_X_whitelist_tx_polls_accepted_count" - metricTxPollsRejected = "avalanche_X_whitelist_tx_polls_rejected_count" -) - -var _ = e2e.DescribeXChain("[WhitelistTx]", func() { - ginkgo.It("can issue whitelist vtx", func() { - if !e2e.GetEnableWhitelistTxTests() { - ginkgo.Skip("whitelist vtx tests are disabled; skipping") - } - - uris := e2e.GetURIs() - gomega.Expect(uris).ShouldNot(gomega.BeEmpty()) - - testKeys, testKeyAddrs, keyChain := e2e.LoadTestKeys() - var baseWallet primary.Wallet - ginkgo.By("collect whitelist vtx metrics", func() { - walletURI := uris[0] - - // 5-second is enough to fetch initial UTXOs for test cluster in "primary.NewWallet" - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) - var err error - baseWallet, err = primary.NewWalletFromURI(ctx, walletURI, keyChain) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - - if baseWallet.P().NetworkID() == constants.MainnetID { - ginkgo.Skip("skipping tests (mainnet)") - } - }) - avaxAssetID := baseWallet.X().AVAXAssetID() - wallets := make([]primary.Wallet, len(testKeys)) - for i := range wallets { - wallets[i] = primary.NewWalletWithOptions( - baseWallet, - common.WithCustomAddresses(ids.ShortSet{ - testKeys[i].PublicKey().Address(): struct{}{}, - }), - ) - } - - allMetrics := []string{ - metricVtxIssueSuccess, - metricVtxIssueFailure, - metricTxProcessing, - metricTxAccepted, - metricTxRejected, - metricTxPollsAccepted, - metricTxPollsRejected, - } - - // URI -> "metric name" -> "metric value" - curMetrics := make(map[string]map[string]float64) - ginkgo.By("collect whitelist vtx metrics", func() { - for _, u := range uris { - ep := u + "/ext/metrics" - - mm, err := tests.GetMetricsValue(ep, allMetrics...) - gomega.Expect(err).Should(gomega.BeNil()) - tests.Outf("{{green}}metrics at %q:{{/}} %v\n", ep, mm) - - if mm[metricTxAccepted] > 0 { - tests.Outf("{{red}}{{bold}}%q already has whitelist vtx!!!{{/}}\n", u) - ginkgo.Skip("the cluster has already accepted whitelist vtx thus skipping") - } - - curMetrics[u] = mm - } - }) - - ginkgo.By("issue regular, virtuous X-Chain tx, before whitelist vtx, should succeed", func() { - balances, err := wallets[0].X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) - key1PrevBalX := balances[avaxAssetID] - tests.Outf("{{green}}first wallet balance:{{/}} %d\n", key1PrevBalX) - - balances, err = wallets[1].X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) - - key2PrevBalX := balances[avaxAssetID] - tests.Outf("{{green}}second wallet balance:{{/}} %d\n", key2PrevBalX) - - transferAmount := key1PrevBalX / 10 - gomega.Expect(transferAmount).Should(gomega.BeNumerically(">", 0.0), "not enough balance in the test wallet") - tests.Outf("{{green}}amount to transfer:{{/}} %d\n", transferAmount) - - tests.Outf("{{blue}}issuing regular, virtuous transaction at %q{{/}}\n", uris[0]) - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) - _, err = wallets[0].X().IssueBaseTx( - []*avax.TransferableOutput{{ - Asset: avax.Asset{ - ID: avaxAssetID, - }, - Out: &secp256k1fx.TransferOutput{ - Amt: transferAmount, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{testKeyAddrs[1]}, - }, - }, - }}, - common.WithContext(ctx), - ) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - - time.Sleep(3 * time.Second) - - balances, err = wallets[0].X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) - key1CurBalX := balances[avaxAssetID] - tests.Outf("{{green}}first wallet balance:{{/}} %d\n", key1CurBalX) - - balances, err = wallets[1].X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) - key2CurBalX := balances[avaxAssetID] - tests.Outf("{{green}}second wallet balance:{{/}} %d\n", key2CurBalX) - - gomega.Expect(key1CurBalX).Should(gomega.Equal(key1PrevBalX - transferAmount - baseWallet.X().BaseTxFee())) - gomega.Expect(key2CurBalX).Should(gomega.Equal(key2PrevBalX + transferAmount)) - }) - - // issue a whitelist vtx to the first node - // to trigger "Notify(common.StopVertex)", "t.issueStopVtx()", and "handleAsyncMsg" - // this is the very first whitelist vtx issue request - // SO THIS SHOULD SUCCEED WITH NO ERROR - ginkgo.By("issue whitelist vtx to the first node", func() { - tests.Outf("{{blue}}{{bold}}issuing whitelist vtx at URI %q at the very first time{{/}}\n", uris[0]) - client := avm.NewClient(uris[0], "X") - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - err := client.IssueStopVertex(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - tests.Outf("{{blue}}issued whitelist vtx at %q{{/}}\n", uris[0]) - }) - - ginkgo.By("accept the whitelist vtx in all nodes", func() { - tests.Outf("{{blue}}waiting before checking the status of whitelist vtx{{/}}\n") - time.Sleep(5 * time.Second) // should NOT take too long for all nodes to accept whitelist vtx - - for _, u := range uris { - ep := u + "/ext/metrics" - mm, err := tests.GetMetricsValue(ep, allMetrics...) - gomega.Expect(err).Should(gomega.BeNil()) - - prev := curMetrics[u] - - // +1 since the local node engine issues a new whitelist vtx - gomega.Expect(mm[metricVtxIssueSuccess]).Should(gomega.Equal(prev[metricVtxIssueSuccess] + 1)) - - // +0 since no node ever failed to issue a whitelist vtx - gomega.Expect(mm[metricVtxIssueFailure]).Should(gomega.Equal(prev[metricVtxIssueFailure])) - - // +0 since the local node snowstorm successfully issued the whitelist tx or received from the first node, and accepted - gomega.Expect(mm[metricTxProcessing]).Should(gomega.Equal(prev[metricTxProcessing])) - - // +1 since the local node snowstorm successfully accepted the whitelist tx or received from the first node - gomega.Expect(mm[metricTxAccepted]).Should(gomega.Equal(prev[metricTxAccepted] + 1)) - gomega.Expect(mm[metricTxPollsAccepted]).Should(gomega.Equal(prev[metricTxPollsAccepted] + 1)) - - // +0 since no node ever rejected a whitelist tx - gomega.Expect(mm[metricTxRejected]).Should(gomega.Equal(prev[metricTxRejected])) - gomega.Expect(mm[metricTxPollsRejected]).Should(gomega.Equal(prev[metricTxPollsRejected])) - - curMetrics[u] = mm - } - }) - - // to trigger "Notify(common.StopVertex)" and "t.issueStopVtx()", or "Put" - // this is the second, conflicting whitelist vtx issue request - // SO THIS MUST FAIL WITH ERROR IN ALL NODES - ginkgo.By("whitelist vtx can't be issued twice in all nodes", func() { - for _, u := range uris { - tests.Outf("{{red}}issuing second whitelist vtx to URI %q{{/}}\n", u) - client := avm.NewClient(u, "X") - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - err := client.IssueStopVertex(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) // issue itself is asynchronous, so the internal error is not exposed! - - // the local node should see updates on the metrics - time.Sleep(3 * time.Second) - - ep := u + "/ext/metrics" - mm, err := tests.GetMetricsValue(ep, allMetrics...) - gomega.Expect(err).Should(gomega.BeNil()) - - prev := curMetrics[u] - - // +0 since no node should ever successfully issue another whitelist vtx - gomega.Expect(mm[metricVtxIssueSuccess]).Should(gomega.Equal(prev[metricVtxIssueSuccess])) - - // +1 since the local node engine failed the conflicting whitelist vtx issue request - gomega.Expect(mm[metricVtxIssueFailure]).Should(gomega.Equal(prev[metricVtxIssueFailure] + 1)) - - // +0 since the local node snowstorm successfully issued the whitelist tx "before", and no whitelist tx is being processed - gomega.Expect(mm[metricTxProcessing]).Should(gomega.Equal(prev[metricTxProcessing])) - - // +0 since the local node snowstorm successfully accepted the whitelist tx "before" - gomega.Expect(mm[metricTxAccepted]).Should(gomega.Equal(prev[metricTxAccepted])) - gomega.Expect(mm[metricTxPollsAccepted]).Should(gomega.Equal(prev[metricTxPollsAccepted])) - - // +0 since the local node snowstorm never rejected a whitelist tx - gomega.Expect(mm[metricTxRejected]).Should(gomega.Equal(prev[metricTxRejected])) - gomega.Expect(mm[metricTxPollsRejected]).Should(gomega.Equal(prev[metricTxPollsRejected])) - - curMetrics[u] = mm - } - }) - - ginkgo.By("issue regular, virtuous X-Chain tx, after whitelist vtx, should fail", func() { - balances, err := wallets[0].X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) - - avaxAssetID := baseWallet.X().AVAXAssetID() - key1PrevBalX := balances[avaxAssetID] - tests.Outf("{{green}}first wallet balance:{{/}} %d\n", key1PrevBalX) - - transferAmount := key1PrevBalX / 10 - gomega.Expect(transferAmount).Should(gomega.BeNumerically(">", 0.0), "not enough balance in the test wallet") - tests.Outf("{{blue}}issuing regular, virtuous transaction at %q{{/}}\n", uris[0]) - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) - _, err = wallets[0].X().IssueBaseTx( - []*avax.TransferableOutput{{ - Asset: avax.Asset{ - ID: avaxAssetID, - }, - Out: &secp256k1fx.TransferOutput{ - Amt: transferAmount, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{testKeyAddrs[1]}, - }, - }, - }}, - common.WithContext(ctx), - ) - cancel() - gomega.Expect(err.Error()).Should(gomega.ContainSubstring(context.DeadlineExceeded.Error())) - - ep := uris[0] + "/ext/metrics" - mm, err := tests.GetMetricsValue(ep, allMetrics...) - gomega.Expect(err).Should(gomega.BeNil()) - - // regular, virtuous transaction should not change whitelist vtx metrics - prev := curMetrics[uris[0]] - gomega.Expect(mm).Should(gomega.Equal(prev)) - curMetrics[uris[0]] = mm - }) - }) -}) diff --git a/avalanchego/tests/e2e/x/transfer/virtuous.go b/avalanchego/tests/e2e/x/transfer/virtuous.go index 778cb1e0..462d677f 100644 --- a/avalanchego/tests/e2e/x/transfer/virtuous.go +++ b/avalanchego/tests/e2e/x/transfer/virtuous.go @@ -33,143 +33,147 @@ const ( const totalRounds = 50 var _ = e2e.DescribeXChain("[Virtuous Transfer Tx AVAX]", func() { - ginkgo.It("can issue a virtuous transfer tx for AVAX asset", func() { - if e2e.GetEnableWhitelistTxTests() { - ginkgo.Skip("whitelist vtx tests are enabled; skipping") - } - - rpcEps := e2e.GetURIs() - gomega.Expect(rpcEps).ShouldNot(gomega.BeEmpty()) - - allMetrics := []string{ - metricVtxProcessing, - metricVtxAccepted, - metricVtxRejected, - } - - runFunc := func(round int) { - tests.Outf("{{green}}\n\n\n\n\n\n---\n[ROUND #%02d]:{{/}}\n", round) + ginkgo.It("can issue a virtuous transfer tx for AVAX asset", + // use this for filtering tests by labels + // ref. https://onsi.github.io/ginkgo/#spec-labels + ginkgo.Label( + "require-network-runner", + "x", + "virtuous-transfer-tx-avax", + ), + func() { + rpcEps := e2e.Env.GetURIs() + gomega.Expect(rpcEps).ShouldNot(gomega.BeEmpty()) + + allMetrics := []string{ + metricVtxProcessing, + metricVtxAccepted, + metricVtxRejected, + } - testKeys, _, _ := e2e.LoadTestKeys() + runFunc := func(round int) { + tests.Outf("{{green}}\n\n\n\n\n\n---\n[ROUND #%02d]:{{/}}\n", round) - needPermute := round > 3 - if needPermute { - rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(testKeys), func(i, j int) { testKeys[i], testKeys[j] = testKeys[j], testKeys[i] }) - } - keyChain := secp256k1fx.NewKeychain(testKeys...) + testKeys, _, _ := e2e.Env.GetTestKeys() - var baseWallet primary.Wallet - var err error - ginkgo.By("setting up a base wallet", func() { - walletURI := rpcEps[0] + needPermute := round > 3 + if needPermute { + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(testKeys), func(i, j int) { testKeys[i], testKeys[j] = testKeys[j], testKeys[i] }) + } + keyChain := secp256k1fx.NewKeychain(testKeys...) + + var baseWallet primary.Wallet + var err error + ginkgo.By("setting up a base wallet", func() { + walletURI := rpcEps[0] + + // 5-second is enough to fetch initial UTXOs for test cluster in "primary.NewWallet" + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + baseWallet, err = primary.NewWalletFromURI(ctx, walletURI, keyChain) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + }) + avaxAssetID := baseWallet.X().AVAXAssetID() + + wallets := make([]primary.Wallet, len(testKeys)) + shortAddrs := make([]ids.ShortID, len(testKeys)) + for i := range wallets { + shortAddrs[i] = testKeys[i].PublicKey().Address() + + wallets[i] = primary.NewWalletWithOptions( + baseWallet, + common.WithCustomAddresses(ids.ShortSet{ + testKeys[i].PublicKey().Address(): struct{}{}, + }), + ) + } - // 5-second is enough to fetch initial UTXOs for test cluster in "primary.NewWallet" - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) - baseWallet, err = primary.NewWalletFromURI(ctx, walletURI, keyChain) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - }) - avaxAssetID := baseWallet.X().AVAXAssetID() - - wallets := make([]primary.Wallet, len(testKeys)) - shortAddrs := make([]ids.ShortID, len(testKeys)) - for i := range wallets { - shortAddrs[i] = testKeys[i].PublicKey().Address() - - wallets[i] = primary.NewWalletWithOptions( - baseWallet, - common.WithCustomAddresses(ids.ShortSet{ - testKeys[i].PublicKey().Address(): struct{}{}, - }), - ) - } + // URI -> "metric name" -> "metric value" + metricsBeforeTx := make(map[string]map[string]float64) + for _, u := range rpcEps { + ep := u + "/ext/metrics" - // URI -> "metric name" -> "metric value" - metricsBeforeTx := make(map[string]map[string]float64) - for _, u := range rpcEps { - ep := u + "/ext/metrics" + mm, err := tests.GetMetricsValue(ep, allMetrics...) + gomega.Expect(err).Should(gomega.BeNil()) + tests.Outf("{{green}}metrics at %q:{{/}} %v\n", ep, mm) - mm, err := tests.GetMetricsValue(ep, allMetrics...) - gomega.Expect(err).Should(gomega.BeNil()) - tests.Outf("{{green}}metrics at %q:{{/}} %v\n", ep, mm) + if mm[metricVtxProcessing] > 0 { + tests.Outf("{{red}}{{bold}}%q already has processing vtx!!!{{/}}\n", u) + ginkgo.Skip("the cluster has already ongoing vtx txs thus skipping to prevent conflicts...") + } - if mm[metricVtxProcessing] > 0 { - tests.Outf("{{red}}{{bold}}%q already has processing vtx!!!{{/}}\n", u) - ginkgo.Skip("the cluster has already ongoing vtx txs thus skipping to prevent conflicts...") + metricsBeforeTx[u] = mm } - metricsBeforeTx[u] = mm - } - - testBalances := make([]uint64, 0) - for i, w := range wallets { - balances, err := w.X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) + testBalances := make([]uint64, 0) + for i, w := range wallets { + balances, err := w.X().Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) - bal := balances[avaxAssetID] - testBalances = append(testBalances, bal) + bal := balances[avaxAssetID] + testBalances = append(testBalances, bal) - fmt.Printf(`CURRENT BALANCE %21d AVAX (SHORT ADDRESS %q) + fmt.Printf(`CURRENT BALANCE %21d AVAX (SHORT ADDRESS %q) `, - bal, - testKeys[i].PublicKey().Address(), - ) - } - fromIdx := -1 - for i := range testBalances { - if fromIdx < 0 && testBalances[i] > 0 { - fromIdx = i - break + bal, + testKeys[i].PublicKey().Address(), + ) + } + fromIdx := -1 + for i := range testBalances { + if fromIdx < 0 && testBalances[i] > 0 { + fromIdx = i + break + } + } + if fromIdx < 0 { + gomega.Expect(fromIdx).Should(gomega.BeNumerically(">", 0), "no address found with non-zero balance") } - } - if fromIdx < 0 { - gomega.Expect(fromIdx).Should(gomega.BeNumerically(">", 0), "no address found with non-zero balance") - } - toIdx := -1 - for i := range testBalances { - // prioritize the address with zero balance - if toIdx < 0 && i != fromIdx && testBalances[i] == 0 { - toIdx = i - break + toIdx := -1 + for i := range testBalances { + // prioritize the address with zero balance + if toIdx < 0 && i != fromIdx && testBalances[i] == 0 { + toIdx = i + break + } + } + if toIdx < 0 { + // no zero balance address, so just transfer between any two addresses + toIdx = (fromIdx + 1) % len(testBalances) } - } - if toIdx < 0 { - // no zero balance address, so just transfer between any two addresses - toIdx = (fromIdx + 1) % len(testBalances) - } - senderOrigBal := testBalances[fromIdx] - receiverOrigBal := testBalances[toIdx] + senderOrigBal := testBalances[fromIdx] + receiverOrigBal := testBalances[toIdx] - amountToTransfer := senderOrigBal / 10 + amountToTransfer := senderOrigBal / 10 - senderNewBal := senderOrigBal - amountToTransfer - baseWallet.X().BaseTxFee() - receiverNewBal := receiverOrigBal + amountToTransfer + senderNewBal := senderOrigBal - amountToTransfer - baseWallet.X().BaseTxFee() + receiverNewBal := receiverOrigBal + amountToTransfer - ginkgo.By("X-Chain transfer with wrong amount must fail", func() { - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) - _, err = wallets[fromIdx].X().IssueBaseTx( - []*avax.TransferableOutput{{ - Asset: avax.Asset{ - ID: avaxAssetID, - }, - Out: &secp256k1fx.TransferOutput{ - Amt: senderOrigBal + 1, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{shortAddrs[toIdx]}, + ginkgo.By("X-Chain transfer with wrong amount must fail", func() { + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + _, err = wallets[fromIdx].X().IssueBaseTx( + []*avax.TransferableOutput{{ + Asset: avax.Asset{ + ID: avaxAssetID, }, - }, - }}, - common.WithContext(ctx), - ) - cancel() - gomega.Expect(err.Error()).Should(gomega.ContainSubstring("insufficient funds")) - }) - - fmt.Printf(`=== + Out: &secp256k1fx.TransferOutput{ + Amt: senderOrigBal + 1, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{shortAddrs[toIdx]}, + }, + }, + }}, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err.Error()).Should(gomega.ContainSubstring("insufficient funds")) + }) + + fmt.Printf(`=== TRANSFERRING FROM [%q] @@ -183,86 +187,86 @@ RECEIVER CURRENT BALANCE : %21d AVAX RECEIVER NEW BALANCE (AFTER) : %21d AVAX === `, - shortAddrs[fromIdx], - senderOrigBal, - senderNewBal, - amountToTransfer, - shortAddrs[toIdx], - receiverOrigBal, - receiverNewBal, - ) - - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) - txID, err := wallets[fromIdx].X().IssueBaseTx( - []*avax.TransferableOutput{{ - Asset: avax.Asset{ - ID: avaxAssetID, - }, - Out: &secp256k1fx.TransferOutput{ - Amt: amountToTransfer, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{shortAddrs[toIdx]}, - }, - }, - }}, - common.WithContext(ctx), - ) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - - balances, err := wallets[fromIdx].X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) - senderCurBalX := balances[avaxAssetID] - tests.Outf("{{green}}first wallet balance:{{/}} %d\n", senderCurBalX) - - balances, err = wallets[toIdx].X().Builder().GetFTBalance() - gomega.Expect(err).Should(gomega.BeNil()) - receiverCurBalX := balances[avaxAssetID] - tests.Outf("{{green}}second wallet balance:{{/}} %d\n", receiverCurBalX) - - gomega.Expect(senderCurBalX).Should(gomega.Equal(senderNewBal)) - gomega.Expect(receiverCurBalX).Should(gomega.Equal(receiverNewBal)) - - for _, u := range rpcEps { - xc := avm.NewClient(u, "X") + shortAddrs[fromIdx], + senderOrigBal, + senderNewBal, + amountToTransfer, + shortAddrs[toIdx], + receiverOrigBal, + receiverNewBal, + ) + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) - status, err := xc.ConfirmTx(ctx, txID, 2*time.Second) + txID, err := wallets[fromIdx].X().IssueBaseTx( + []*avax.TransferableOutput{{ + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: amountToTransfer, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{shortAddrs[toIdx]}, + }, + }, + }}, + common.WithContext(ctx), + ) cancel() gomega.Expect(err).Should(gomega.BeNil()) - gomega.Expect(status).Should(gomega.Equal(choices.Accepted)) - } - for _, u := range rpcEps { - xc := avm.NewClient(u, "X") - ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) - status, err := xc.ConfirmTx(ctx, txID, 2*time.Second) - cancel() + balances, err := wallets[fromIdx].X().Builder().GetFTBalance() gomega.Expect(err).Should(gomega.BeNil()) - gomega.Expect(status).Should(gomega.Equal(choices.Accepted)) + senderCurBalX := balances[avaxAssetID] + tests.Outf("{{green}}first wallet balance:{{/}} %d\n", senderCurBalX) - ep := u + "/ext/metrics" - mm, err := tests.GetMetricsValue(ep, allMetrics...) + balances, err = wallets[toIdx].X().Builder().GetFTBalance() gomega.Expect(err).Should(gomega.BeNil()) + receiverCurBalX := balances[avaxAssetID] + tests.Outf("{{green}}second wallet balance:{{/}} %d\n", receiverCurBalX) + + gomega.Expect(senderCurBalX).Should(gomega.Equal(senderNewBal)) + gomega.Expect(receiverCurBalX).Should(gomega.Equal(receiverNewBal)) + + for _, u := range rpcEps { + xc := avm.NewClient(u, "X") + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + status, err := xc.ConfirmTx(ctx, txID, 2*time.Second) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(status).Should(gomega.Equal(choices.Accepted)) + } - prev := metricsBeforeTx[u] + for _, u := range rpcEps { + xc := avm.NewClient(u, "X") + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + status, err := xc.ConfirmTx(ctx, txID, 2*time.Second) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(status).Should(gomega.Equal(choices.Accepted)) - // +0 since X-chain tx must have been processed and accepted by now - gomega.Expect(mm[metricVtxProcessing]).Should(gomega.Equal(prev[metricVtxProcessing])) + ep := u + "/ext/metrics" + mm, err := tests.GetMetricsValue(ep, allMetrics...) + gomega.Expect(err).Should(gomega.BeNil()) - // +1 since X-chain tx must have been accepted by now - gomega.Expect(mm[metricVtxAccepted]).Should(gomega.Equal(prev[metricVtxAccepted] + 1)) + prev := metricsBeforeTx[u] - // +0 since virtuous X-chain tx must not be rejected - gomega.Expect(mm[metricVtxRejected]).Should(gomega.Equal(prev[metricVtxRejected])) + // +0 since X-chain tx must have been processed and accepted by now + gomega.Expect(mm[metricVtxProcessing]).Should(gomega.Equal(prev[metricVtxProcessing])) - metricsBeforeTx[u] = mm + // +1 since X-chain tx must have been accepted by now + gomega.Expect(mm[metricVtxAccepted]).Should(gomega.Equal(prev[metricVtxAccepted] + 1)) + + // +0 since virtuous X-chain tx must not be rejected + gomega.Expect(mm[metricVtxRejected]).Should(gomega.Equal(prev[metricVtxRejected])) + + metricsBeforeTx[u] = mm + } } - } - for i := 0; i < totalRounds; i++ { - runFunc(i) - time.Sleep(time.Second) - } - }) + for i := 0; i < totalRounds; i++ { + runFunc(i) + time.Sleep(time.Second) + } + }) }) diff --git a/avalanchego/tests/e2e/x/whitelist-vtx/suites.go b/avalanchego/tests/e2e/x/whitelist-vtx/suites.go new file mode 100644 index 00000000..72c24380 --- /dev/null +++ b/avalanchego/tests/e2e/x/whitelist-vtx/suites.go @@ -0,0 +1,284 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// Implements X-Chain whitelist vtx (stop vertex) tests. +package whitelistvtx + +import ( + "context" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/tests" + "github.com/ava-labs/avalanchego/tests/e2e" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/avm" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" + + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +const ( + metricVtxIssueSuccess = "avalanche_X_whitelist_vtx_issue_success" + metricVtxIssueFailure = "avalanche_X_whitelist_vtx_issue_failure" + metricTxProcessing = "avalanche_X_whitelist_tx_processing" + metricTxAccepted = "avalanche_X_whitelist_tx_accepted_count" + metricTxRejected = "avalanche_X_whitelist_tx_rejected_count" + metricTxPollsAccepted = "avalanche_X_whitelist_tx_polls_accepted_count" + metricTxPollsRejected = "avalanche_X_whitelist_tx_polls_rejected_count" +) + +var _ = e2e.DescribeXChain("[WhitelistTx]", func() { + ginkgo.It("can issue whitelist vtx", + // use this for filtering tests by labels + // ref. https://onsi.github.io/ginkgo/#spec-labels + ginkgo.Label( + "require-network-runner", + "x", + "whitelist-tx", + ), + func() { + uris := e2e.Env.GetURIs() + gomega.Expect(uris).ShouldNot(gomega.BeEmpty()) + + testKeys, testKeyAddrs, keyChain := e2e.Env.GetTestKeys() + var baseWallet primary.Wallet + ginkgo.By("collect whitelist vtx metrics", func() { + walletURI := uris[0] + + // 5-second is enough to fetch initial UTXOs for test cluster in "primary.NewWallet" + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultWalletCreationTimeout) + var err error + baseWallet, err = primary.NewWalletFromURI(ctx, walletURI, keyChain) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + if baseWallet.P().NetworkID() == constants.MainnetID { + ginkgo.Skip("skipping tests (mainnet)") + } + }) + avaxAssetID := baseWallet.X().AVAXAssetID() + wallets := make([]primary.Wallet, len(testKeys)) + for i := range wallets { + wallets[i] = primary.NewWalletWithOptions( + baseWallet, + common.WithCustomAddresses(ids.ShortSet{ + testKeys[i].PublicKey().Address(): struct{}{}, + }), + ) + } + + allMetrics := []string{ + metricVtxIssueSuccess, + metricVtxIssueFailure, + metricTxProcessing, + metricTxAccepted, + metricTxRejected, + metricTxPollsAccepted, + metricTxPollsRejected, + } + + // URI -> "metric name" -> "metric value" + curMetrics := make(map[string]map[string]float64) + ginkgo.By("collect whitelist vtx metrics", func() { + for _, u := range uris { + ep := u + "/ext/metrics" + + mm, err := tests.GetMetricsValue(ep, allMetrics...) + gomega.Expect(err).Should(gomega.BeNil()) + tests.Outf("{{green}}metrics at %q:{{/}} %v\n", ep, mm) + + if mm[metricTxAccepted] > 0 { + tests.Outf("{{red}}{{bold}}%q already has whitelist vtx!!!{{/}}\n", u) + ginkgo.Skip("the cluster has already accepted whitelist vtx thus skipping") + } + + curMetrics[u] = mm + } + }) + + ginkgo.By("issue regular, virtuous X-Chain tx, before whitelist vtx, should succeed", func() { + balances, err := wallets[0].X().Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + key1PrevBalX := balances[avaxAssetID] + tests.Outf("{{green}}first wallet balance:{{/}} %d\n", key1PrevBalX) + + balances, err = wallets[1].X().Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + + key2PrevBalX := balances[avaxAssetID] + tests.Outf("{{green}}second wallet balance:{{/}} %d\n", key2PrevBalX) + + transferAmount := key1PrevBalX / 10 + gomega.Expect(transferAmount).Should(gomega.BeNumerically(">", 0.0), "not enough balance in the test wallet") + tests.Outf("{{green}}amount to transfer:{{/}} %d\n", transferAmount) + + tests.Outf("{{blue}}issuing regular, virtuous transaction at %q{{/}}\n", uris[0]) + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + _, err = wallets[0].X().IssueBaseTx( + []*avax.TransferableOutput{{ + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: transferAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeyAddrs[1]}, + }, + }, + }}, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + + time.Sleep(3 * time.Second) + + balances, err = wallets[0].X().Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + key1CurBalX := balances[avaxAssetID] + tests.Outf("{{green}}first wallet balance:{{/}} %d\n", key1CurBalX) + + balances, err = wallets[1].X().Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + key2CurBalX := balances[avaxAssetID] + tests.Outf("{{green}}second wallet balance:{{/}} %d\n", key2CurBalX) + + gomega.Expect(key1CurBalX).Should(gomega.Equal(key1PrevBalX - transferAmount - baseWallet.X().BaseTxFee())) + gomega.Expect(key2CurBalX).Should(gomega.Equal(key2PrevBalX + transferAmount)) + }) + + // issue a whitelist vtx to the first node + // to trigger "Notify(common.StopVertex)", "t.issueStopVtx()", and "handleAsyncMsg" + // this is the very first whitelist vtx issue request + // SO THIS SHOULD SUCCEED WITH NO ERROR + ginkgo.By("issue whitelist vtx to the first node", func() { + tests.Outf("{{blue}}{{bold}}issuing whitelist vtx at URI %q at the very first time{{/}}\n", uris[0]) + client := avm.NewClient(uris[0], "X") + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + err := client.IssueStopVertex(ctx) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) + tests.Outf("{{blue}}issued whitelist vtx at %q{{/}}\n", uris[0]) + }) + + ginkgo.By("accept the whitelist vtx in all nodes", func() { + tests.Outf("{{blue}}waiting before checking the status of whitelist vtx{{/}}\n") + time.Sleep(5 * time.Second) // should NOT take too long for all nodes to accept whitelist vtx + + for _, u := range uris { + ep := u + "/ext/metrics" + mm, err := tests.GetMetricsValue(ep, allMetrics...) + gomega.Expect(err).Should(gomega.BeNil()) + + prev := curMetrics[u] + + // +1 since the local node engine issues a new whitelist vtx + gomega.Expect(mm[metricVtxIssueSuccess]).Should(gomega.Equal(prev[metricVtxIssueSuccess] + 1)) + + // +0 since no node ever failed to issue a whitelist vtx + gomega.Expect(mm[metricVtxIssueFailure]).Should(gomega.Equal(prev[metricVtxIssueFailure])) + + // +0 since the local node snowstorm successfully issued the whitelist tx or received from the first node, and accepted + gomega.Expect(mm[metricTxProcessing]).Should(gomega.Equal(prev[metricTxProcessing])) + + // +1 since the local node snowstorm successfully accepted the whitelist tx or received from the first node + gomega.Expect(mm[metricTxAccepted]).Should(gomega.Equal(prev[metricTxAccepted] + 1)) + gomega.Expect(mm[metricTxPollsAccepted]).Should(gomega.Equal(prev[metricTxPollsAccepted] + 1)) + + // +0 since no node ever rejected a whitelist tx + gomega.Expect(mm[metricTxRejected]).Should(gomega.Equal(prev[metricTxRejected])) + gomega.Expect(mm[metricTxPollsRejected]).Should(gomega.Equal(prev[metricTxPollsRejected])) + + curMetrics[u] = mm + } + }) + + // to trigger "Notify(common.StopVertex)" and "t.issueStopVtx()", or "Put" + // this is the second, conflicting whitelist vtx issue request + // SO THIS MUST FAIL WITH ERROR IN ALL NODES + ginkgo.By("whitelist vtx can't be issued twice in all nodes", func() { + for _, u := range uris { + tests.Outf("{{red}}issuing second whitelist vtx to URI %q{{/}}\n", u) + client := avm.NewClient(u, "X") + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + err := client.IssueStopVertex(ctx) + cancel() + gomega.Expect(err).Should(gomega.BeNil()) // issue itself is asynchronous, so the internal error is not exposed! + + // the local node should see updates on the metrics + time.Sleep(3 * time.Second) + + ep := u + "/ext/metrics" + mm, err := tests.GetMetricsValue(ep, allMetrics...) + gomega.Expect(err).Should(gomega.BeNil()) + + prev := curMetrics[u] + + // +0 since no node should ever successfully issue another whitelist vtx + gomega.Expect(mm[metricVtxIssueSuccess]).Should(gomega.Equal(prev[metricVtxIssueSuccess])) + + // +1 since the local node engine failed the conflicting whitelist vtx issue request + gomega.Expect(mm[metricVtxIssueFailure]).Should(gomega.Equal(prev[metricVtxIssueFailure] + 1)) + + // +0 since the local node snowstorm successfully issued the whitelist tx "before", and no whitelist tx is being processed + gomega.Expect(mm[metricTxProcessing]).Should(gomega.Equal(prev[metricTxProcessing])) + + // +0 since the local node snowstorm successfully accepted the whitelist tx "before" + gomega.Expect(mm[metricTxAccepted]).Should(gomega.Equal(prev[metricTxAccepted])) + gomega.Expect(mm[metricTxPollsAccepted]).Should(gomega.Equal(prev[metricTxPollsAccepted])) + + // +0 since the local node snowstorm never rejected a whitelist tx + gomega.Expect(mm[metricTxRejected]).Should(gomega.Equal(prev[metricTxRejected])) + gomega.Expect(mm[metricTxPollsRejected]).Should(gomega.Equal(prev[metricTxPollsRejected])) + + curMetrics[u] = mm + } + }) + + ginkgo.By("issue regular, virtuous X-Chain tx, after whitelist vtx, should fail", func() { + balances, err := wallets[0].X().Builder().GetFTBalance() + gomega.Expect(err).Should(gomega.BeNil()) + + avaxAssetID := baseWallet.X().AVAXAssetID() + key1PrevBalX := balances[avaxAssetID] + tests.Outf("{{green}}first wallet balance:{{/}} %d\n", key1PrevBalX) + + transferAmount := key1PrevBalX / 10 + gomega.Expect(transferAmount).Should(gomega.BeNumerically(">", 0.0), "not enough balance in the test wallet") + tests.Outf("{{blue}}issuing regular, virtuous transaction at %q{{/}}\n", uris[0]) + ctx, cancel := context.WithTimeout(context.Background(), e2e.DefaultConfirmTxTimeout) + _, err = wallets[0].X().IssueBaseTx( + []*avax.TransferableOutput{{ + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: transferAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeyAddrs[1]}, + }, + }, + }}, + common.WithContext(ctx), + ) + cancel() + gomega.Expect(err.Error()).Should(gomega.ContainSubstring(context.DeadlineExceeded.Error())) + + ep := uris[0] + "/ext/metrics" + mm, err := tests.GetMetricsValue(ep, allMetrics...) + gomega.Expect(err).Should(gomega.BeNil()) + + // regular, virtuous transaction should not change whitelist vtx metrics + prev := curMetrics[uris[0]] + gomega.Expect(mm).Should(gomega.Equal(prev)) + curMetrics[uris[0]] = mm + }) + }) +}) diff --git a/avalanchego/tests/upgrade/upgrade_test.go b/avalanchego/tests/upgrade/upgrade_test.go index 80f10fc2..1af66894 100644 --- a/avalanchego/tests/upgrade/upgrade_test.go +++ b/avalanchego/tests/upgrade/upgrade_test.go @@ -95,7 +95,7 @@ var _ = ginkgo.BeforeSuite(func() { ) cancel() gomega.Expect(err).Should(gomega.BeNil()) - tests.Outf("{{green}}successfully started network-runner :{{/}} %+v\n", resp.ClusterInfo.NodeNames) + tests.Outf("{{green}}successfully started network-runner: {{/}} %+v\n", resp.ClusterInfo.NodeNames) // start is async, so wait some time for cluster health time.Sleep(time.Minute) diff --git a/avalanchego/utils/atomic_bool.go b/avalanchego/utils/atomic_bool.go index 1bccdf1e..c01008ac 100644 --- a/avalanchego/utils/atomic_bool.go +++ b/avalanchego/utils/atomic_bool.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utils diff --git a/avalanchego/utils/atomic_interface.go b/avalanchego/utils/atomic_interface.go index 1ee1a74e..d3c239aa 100644 --- a/avalanchego/utils/atomic_interface.go +++ b/avalanchego/utils/atomic_interface.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utils diff --git a/avalanchego/utils/atomic_interface_test.go b/avalanchego/utils/atomic_interface_test.go index 6b87bdd8..2897e4dd 100644 --- a/avalanchego/utils/atomic_interface_test.go +++ b/avalanchego/utils/atomic_interface_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utils diff --git a/avalanchego/utils/beacon/beacon.go b/avalanchego/utils/beacon/beacon.go index bb8aabde..ecaaa613 100644 --- a/avalanchego/utils/beacon/beacon.go +++ b/avalanchego/utils/beacon/beacon.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package beacon diff --git a/avalanchego/utils/beacon/set.go b/avalanchego/utils/beacon/set.go index 5bc3a80d..305c2d52 100644 --- a/avalanchego/utils/beacon/set.go +++ b/avalanchego/utils/beacon/set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package beacon diff --git a/avalanchego/utils/beacon/set_test.go b/avalanchego/utils/beacon/set_test.go index 6e87eb6b..c4eab3a8 100644 --- a/avalanchego/utils/beacon/set_test.go +++ b/avalanchego/utils/beacon/set_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package beacon diff --git a/avalanchego/utils/bloom/bloom_filter.go b/avalanchego/utils/bloom/bloom_filter.go index 7105c694..66c4039b 100644 --- a/avalanchego/utils/bloom/bloom_filter.go +++ b/avalanchego/utils/bloom/bloom_filter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bloom diff --git a/avalanchego/utils/bloom/bloom_filter_test.go b/avalanchego/utils/bloom/bloom_filter_test.go index baaee739..a02d18ab 100644 --- a/avalanchego/utils/bloom/bloom_filter_test.go +++ b/avalanchego/utils/bloom/bloom_filter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bloom diff --git a/avalanchego/utils/bloom/map_filter.go b/avalanchego/utils/bloom/map_filter.go index 5188f45f..98684a87 100644 --- a/avalanchego/utils/bloom/map_filter.go +++ b/avalanchego/utils/bloom/map_filter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package bloom diff --git a/avalanchego/utils/buffer/unbounded_queue.go b/avalanchego/utils/buffer/unbounded_queue.go new file mode 100644 index 00000000..ee578cbe --- /dev/null +++ b/avalanchego/utils/buffer/unbounded_queue.go @@ -0,0 +1,103 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package buffer + +import "github.com/ava-labs/avalanchego/utils" + +const defaultInitSize = 32 + +// An unbounded queue. +// Not safe for concurrent access. +type UnboundedQueue[T any] interface { + Enqueue(T) + // Returns false if the queue is empty. + Dequeue() (T, bool) + // Returns the oldest element without removing it. + // Returns false if the queue is empty. + PeekHead() (T, bool) + // Returns the newest without removing it. + // Returns false if the queue is empty. + PeekTail() (T, bool) + Len() int +} + +// Returns a new unbounded queue with the given initial slice size. +// Note that the returned queue is always empty -- [initSize] is just +// a hint to prevent unnecessary resizing. +func NewUnboundedSliceQueue[T any](initSize int) UnboundedQueue[T] { + if initSize <= 0 { + initSize = defaultInitSize + } + return &unboundedSliceQueue[T]{ + // Note that [initSize] must be > 0 to satisfy invariant (5). + data: make([]T, initSize), + } +} + +// Invariants after each function call and before the first call: +// (1) If head == tail then the queue is empty +// (2) If head < tail then the queue is data[head:tail] +// (3) If head > tail then the queue is data[head:len(data)] + data[0:tail] +// (4) The next element to be dequeued is data[head] +// (5) The next element will be enqueued at data[tail] +// (6) There are [size] elements in the queue. +type unboundedSliceQueue[T any] struct { + size, head, tail int + data []T +} + +func (b *unboundedSliceQueue[T]) Enqueue(elt T) { + // Invariant (5) says it's safe to place the element without resizing. + b.data[b.tail] = elt + b.size++ + b.tail++ + b.tail %= len(b.data) + + if b.head != b.tail { + return + } + // Invariant (1) says if the head and the tail are equal then the queue is empty. + // It isn't -- we just enqueued an element -- so we need to resize to honor invariant (1). + newData := make([]T, b.size*2) + copy(newData, b.data[b.head:]) + numCopied := len(b.data) - b.head + copy(newData[numCopied:], b.data[:b.tail]) + b.data = newData + b.head = 0 + b.tail = b.size +} + +func (b *unboundedSliceQueue[T]) Dequeue() (T, bool) { + if b.size == 0 { + return utils.Zero[T](), false + } + elt := b.data[b.head] + // Zero out to prevent memory leak. + b.data[b.head] = utils.Zero[T]() + b.size-- + b.head++ + b.head %= len(b.data) + return elt, true +} + +func (b *unboundedSliceQueue[T]) PeekHead() (T, bool) { + if b.size == 0 { + return utils.Zero[T](), false + } + return b.data[b.head], true +} + +func (b *unboundedSliceQueue[T]) PeekTail() (T, bool) { + if b.size == 0 { + return utils.Zero[T](), false + } + if b.tail == 0 { + return b.data[len(b.data)-1], true + } + return b.data[b.tail-1], true +} + +func (b *unboundedSliceQueue[T]) Len() int { + return b.size +} diff --git a/avalanchego/utils/buffer/unbounded_queue_test.go b/avalanchego/utils/buffer/unbounded_queue_test.go new file mode 100644 index 00000000..4560b899 --- /dev/null +++ b/avalanchego/utils/buffer/unbounded_queue_test.go @@ -0,0 +1,306 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package buffer + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUnboundedSliceQueue(t *testing.T) { + require := require.New(t) + + // Starts empty. + bIntf := NewUnboundedSliceQueue[int](1) + b, ok := bIntf.(*unboundedSliceQueue[int]) + require.True(ok) + require.Equal(0, bIntf.Len()) + require.Equal(1, len(b.data)) + require.Equal(0, b.head) + require.Equal(0, b.tail) + // queue is [EMPTY] + + _, ok = b.Dequeue() + require.False(ok) + _, ok = b.PeekHead() + require.False(ok) + _, ok = b.PeekTail() + require.False(ok) + + // This causes a resize + b.Enqueue(1) + require.Equal(1, b.Len()) + require.Equal(2, len(b.data)) + require.Equal(0, b.head) + require.Equal(1, b.tail) + // queue is [1,EMPTY] + + got, ok := b.PeekHead() + require.True(ok) + require.Equal(1, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(1, got) + + // This causes a resize + b.Enqueue(2) + require.Equal(2, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(0, b.head) + require.Equal(2, b.tail) + // queue is [1,2,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(1, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(2, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(1, got) + require.Equal(1, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(1, b.head) + require.Equal(2, b.tail) + // queue is [EMPTY,2,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(2, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(2, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(2, got) + require.Equal(0, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(2, b.head) + require.Equal(2, b.tail) + // queue is [EMPTY,EMPTY,EMPTY,EMPTY] + + _, ok = b.Dequeue() + require.False(ok) + _, ok = b.PeekHead() + require.False(ok) + _, ok = b.PeekTail() + require.False(ok) + + b.Enqueue(3) + require.Equal(1, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(2, b.head) + require.Equal(3, b.tail) + // queue is [EMPTY,EMPTY,3,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(3, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(3, got) + + b.Enqueue(4) + require.Equal(2, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(2, b.head) + require.Equal(0, b.tail) + // queue is [EMPTY,EMPTY,3,4] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(3, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(4, got) + + // This tests tail wrap around. + b.Enqueue(5) + require.Equal(3, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(2, b.head) + require.Equal(1, b.tail) + // queue is [5,EMPTY,3,4] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(3, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(5, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(3, got) + require.Equal(2, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(3, b.head) + require.Equal(1, b.tail) + // queue is [5,EMPTY,EMPTY,4] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(4, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(5, got) + + // This tests head wrap around. + got, ok = b.Dequeue() + require.True(ok) + require.Equal(4, got) + require.Equal(1, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(0, b.head) + require.Equal(1, b.tail) + // queue is [5,EMPTY,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(5, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(5, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(5, got) + require.Equal(0, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(1, b.head) + require.Equal(1, b.tail) + // queue is [EMPTY,EMPTY,EMPTY,EMPTY] + + _, ok = b.Dequeue() + require.False(ok) + _, ok = b.PeekHead() + require.False(ok) + _, ok = b.PeekTail() + require.False(ok) + + b.Enqueue(6) + require.Equal(1, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(1, b.head) + require.Equal(2, b.tail) + // queue is [EMPTY,6,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(6, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(6, got) + + b.Enqueue(7) + require.Equal(2, b.Len()) + require.Equal(1, b.head) + require.Equal(3, b.tail) + // queue is [EMPTY,6,7,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(6, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(7, got) + + b.Enqueue(8) + require.Equal(3, b.Len()) + require.Equal(4, len(b.data)) + require.Equal(1, b.head) + require.Equal(0, b.tail) + // queue is [EMPTY,6,7,8] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(6, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(8, got) + + // This causes a resize + b.Enqueue(9) + require.Equal(4, b.Len()) + require.Equal(8, len(b.data)) + require.Equal(0, b.head) + require.Equal(4, b.tail) + // queue is [6,7,8,9,EMPTY,EMPTY,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(6, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(9, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(6, got) + // queue is [EMPTY,7,8,9,EMPTY,EMPTY,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(7, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(9, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(7, got) + // queue is [EMPTY,EMPTY,8,9,EMPTY,EMPTY,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(8, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(9, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(8, got) + // queue is [EMPTY,EMPTY,EMPTY,9,EMPTY,EMPTY,EMPTY,EMPTY] + + got, ok = b.PeekHead() + require.True(ok) + require.Equal(9, got) + + got, ok = b.PeekTail() + require.True(ok) + require.Equal(9, got) + + got, ok = b.Dequeue() + require.True(ok) + require.Equal(9, got) + require.Equal(0, b.Len()) + require.Equal(8, len(b.data)) + require.Equal(4, b.head) + require.Equal(4, b.tail) + // queue is [EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY] + + _, ok = b.PeekHead() + require.False(ok) + _, ok = b.PeekTail() + require.False(ok) +} diff --git a/avalanchego/utils/bytes.go b/avalanchego/utils/bytes.go index c74f71ef..17ae9686 100644 --- a/avalanchego/utils/bytes.go +++ b/avalanchego/utils/bytes.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utils diff --git a/avalanchego/utils/bytes_test.go b/avalanchego/utils/bytes_test.go index a3a3cb28..fc516061 100644 --- a/avalanchego/utils/bytes_test.go +++ b/avalanchego/utils/bytes_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utils diff --git a/avalanchego/utils/cb58/cb58.go b/avalanchego/utils/cb58/cb58.go index 8969b101..8be51e65 100644 --- a/avalanchego/utils/cb58/cb58.go +++ b/avalanchego/utils/cb58/cb58.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cb58 diff --git a/avalanchego/utils/cb58/cb58_test.go b/avalanchego/utils/cb58/cb58_test.go index 42615d12..ac0c2dca 100644 --- a/avalanchego/utils/cb58/cb58_test.go +++ b/avalanchego/utils/cb58/cb58_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package cb58 diff --git a/avalanchego/utils/compression/compressor.go b/avalanchego/utils/compression/compressor.go index 31b22e4d..5cc4d95b 100644 --- a/avalanchego/utils/compression/compressor.go +++ b/avalanchego/utils/compression/compressor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package compression diff --git a/avalanchego/utils/compression/gzip_compressor.go b/avalanchego/utils/compression/gzip_compressor.go index be93f37f..63b806c5 100644 --- a/avalanchego/utils/compression/gzip_compressor.go +++ b/avalanchego/utils/compression/gzip_compressor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package compression @@ -6,14 +6,20 @@ package compression import ( "bytes" "compress/gzip" + "errors" "fmt" "io" + "math" "sync" "github.com/ava-labs/avalanchego/utils" ) -var _ Compressor = &gzipCompressor{} +var ( + _ Compressor = &gzipCompressor{} + + ErrInvalidMaxSizeGzipCompressor = errors.New("invalid gzip compressor max size") +) type gzipCompressor struct { maxSize int64 @@ -76,7 +82,15 @@ func (g *gzipCompressor) Decompress(msg []byte) ([]byte, error) { } // NewGzipCompressor returns a new gzip Compressor that compresses -func NewGzipCompressor(maxSize int64) Compressor { +func NewGzipCompressor(maxSize int64) (Compressor, error) { + if maxSize == math.MaxInt64 { + // "Decompress" creates "io.LimitReader" with max size + 1: + // if the max size + 1 overflows, "io.LimitReader" reads nothing + // returning 0 byte for the decompress call + // require max size num sigs", + setup: func(require *require.Assertions) ([]*PublicKey, []*Signature, []byte) { + sk0, err := NewSecretKey() + require.NoError(err) + sk1, err := NewSecretKey() + require.NoError(err) + sk2, err := NewSecretKey() + require.NoError(err) + + pks := []*PublicKey{ + PublicFromSecretKey(sk0), + PublicFromSecretKey(sk1), + PublicFromSecretKey(sk2), + } + + msg := utils.RandomBytes(1234) + + sigs := []*Signature{ + Sign(sk0, msg), + Sign(sk1, msg), + } + + return pks, sigs, msg + }, + expectedValid: false, + }, + { + name: "num pubkeys < num sigs", + setup: func(require *require.Assertions) ([]*PublicKey, []*Signature, []byte) { + sk0, err := NewSecretKey() + require.NoError(err) + sk1, err := NewSecretKey() + require.NoError(err) + sk2, err := NewSecretKey() + require.NoError(err) + + pks := []*PublicKey{ + PublicFromSecretKey(sk0), + PublicFromSecretKey(sk1), + } + + msg := utils.RandomBytes(1234) + + sigs := []*Signature{ + Sign(sk0, msg), + Sign(sk1, msg), + Sign(sk2, msg), + } + + return pks, sigs, msg + }, + expectedValid: false, + }, + { + name: "no pub keys", + setup: func(require *require.Assertions) ([]*PublicKey, []*Signature, []byte) { + sk0, err := NewSecretKey() + require.NoError(err) + sk1, err := NewSecretKey() + require.NoError(err) + sk2, err := NewSecretKey() + require.NoError(err) + + msg := utils.RandomBytes(1234) + + sigs := []*Signature{ + Sign(sk0, msg), + Sign(sk1, msg), + Sign(sk2, msg), + } + + return nil, sigs, msg + }, + expectedPubKeyAggError: errNoPublicKeys, + expectedValid: false, + }, + { + name: "no sigs", + setup: func(require *require.Assertions) ([]*PublicKey, []*Signature, []byte) { + sk0, err := NewSecretKey() + require.NoError(err) + sk1, err := NewSecretKey() + require.NoError(err) + sk2, err := NewSecretKey() + require.NoError(err) + + pks := []*PublicKey{ + PublicFromSecretKey(sk0), + PublicFromSecretKey(sk1), + PublicFromSecretKey(sk2), + } + + msg := utils.RandomBytes(1234) + return pks, nil, msg + }, + expectedSigAggError: errNoSignatures, + expectedValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + pks, sigs, msg := tt.setup(require) + + aggSig, err := AggregateSignatures(sigs) + require.ErrorIs(err, tt.expectedSigAggError) + + aggPK, err := AggregatePublicKeys(pks) + require.ErrorIs(err, tt.expectedPubKeyAggError) + + valid := Verify(aggPK, aggSig, msg) + require.Equal(tt.expectedValid, valid) + }) + } +} + +func TestAggregationThreshold(t *testing.T) { + require := require.New(t) + + // People in the network would privately generate their secret keys + sk0, err := NewSecretKey() + require.NoError(err) + sk1, err := NewSecretKey() + require.NoError(err) + sk2, err := NewSecretKey() + require.NoError(err) + + // All the public keys would be registered on chain + pks := []*PublicKey{ + PublicFromSecretKey(sk0), + PublicFromSecretKey(sk1), + PublicFromSecretKey(sk2), + } + + // The transaction's unsigned bytes are publicly known. + msg := utils.RandomBytes(1234) + + // People may attempt time sign the transaction. + sigs := []*Signature{ + Sign(sk0, msg), + Sign(sk1, msg), + Sign(sk2, msg), + } + + // The signed transaction would specify which of the public keys have been + // used to sign it. The aggregator should verify each individual signature, + // until it has found a sufficient threshold of valid signatures. + var ( + indices = []int{0, 2} + filteredPKs = make([]*PublicKey, len(indices)) + filteredSigs = make([]*Signature, len(indices)) + ) + for i, index := range indices { + pk := pks[index] + filteredPKs[i] = pk + sig := sigs[index] + filteredSigs[i] = sig + + valid := Verify(pk, sig, msg) + require.True(valid) + } + + // Once the aggregator has the required threshold of signatures, it can + // aggregate the signatures. + aggregatedSig, err := AggregateSignatures(filteredSigs) + require.NoError(err) + + // For anyone looking for a proof of the aggregated signature's correctness, + // they can aggregate the public keys and verify the aggregated signature. + aggregatedPK, err := AggregatePublicKeys(filteredPKs) + require.NoError(err) + + valid := Verify(aggregatedPK, aggregatedSig, msg) + require.True(valid) +} + +func TestVerify(t *testing.T) { + type test struct { + name string + setup func(*require.Assertions) (pk *PublicKey, sig *Signature, msg []byte) + expectedValid bool + } + + tests := []test{ + { + name: "valid", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk) + msg := utils.RandomBytes(1234) + sig := Sign(sk, msg) + return pk, sig, msg + }, + expectedValid: true, + }, + { + name: "wrong message", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk) + msg := utils.RandomBytes(1234) + sig := Sign(sk, msg) + msg[0]++ + return pk, sig, msg + }, + expectedValid: false, + }, + { + name: "wrong pub key", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + msg := utils.RandomBytes(1234) + sig := Sign(sk, msg) + + sk2, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk2) + return pk, sig, msg + }, + expectedValid: false, + }, + { + name: "wrong sig", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk) + msg := utils.RandomBytes(1234) + + msg2 := utils.RandomBytes(1234) + sig2 := Sign(sk, msg2) + return pk, sig2, msg + }, + expectedValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + pk, sig, msg := tt.setup(require) + valid := Verify(pk, sig, msg) + require.Equal(tt.expectedValid, valid) + valid = VerifyProofOfPossession(pk, sig, msg) + require.False(valid) + }) + } +} + +func TestVerifyProofOfPossession(t *testing.T) { + type test struct { + name string + setup func(*require.Assertions) (pk *PublicKey, sig *Signature, msg []byte) + expectedValid bool + } + + tests := []test{ + { + name: "valid", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk) + msg := utils.RandomBytes(1234) + sig := SignProofOfPossession(sk, msg) + return pk, sig, msg + }, + expectedValid: true, + }, + { + name: "wrong message", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk) + msg := utils.RandomBytes(1234) + sig := SignProofOfPossession(sk, msg) + msg[0]++ + return pk, sig, msg + }, + expectedValid: false, + }, + { + name: "wrong pub key", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + msg := utils.RandomBytes(1234) + sig := SignProofOfPossession(sk, msg) + + sk2, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk2) + return pk, sig, msg + }, + expectedValid: false, + }, + { + name: "wrong sig", + setup: func(require *require.Assertions) (*PublicKey, *Signature, []byte) { + sk, err := NewSecretKey() + require.NoError(err) + pk := PublicFromSecretKey(sk) + msg := utils.RandomBytes(1234) + + msg2 := utils.RandomBytes(1234) + sig2 := SignProofOfPossession(sk, msg2) + return pk, sig2, msg + }, + expectedValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + pk, sig, msg := tt.setup(require) + valid := VerifyProofOfPossession(pk, sig, msg) + require.Equal(tt.expectedValid, valid) + valid = Verify(pk, sig, msg) + require.False(valid) + }) + } +} diff --git a/avalanchego/utils/crypto/bls/public.go b/avalanchego/utils/crypto/bls/public.go new file mode 100644 index 00000000..88ed660f --- /dev/null +++ b/avalanchego/utils/crypto/bls/public.go @@ -0,0 +1,72 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package bls + +import ( + "errors" + + blst "github.com/supranational/blst/bindings/go" +) + +const PublicKeyLen = blst.BLST_P1_COMPRESS_BYTES + +var ( + errFailedPublicKeyDecompress = errors.New("couldn't decompress public key") + errInvalidPublicKey = errors.New("invalid public key") + errNoPublicKeys = errors.New("no public keys") + errFailedPublicKeyAggregation = errors.New("couldn't aggregate public keys") +) + +type ( + PublicKey = blst.P1Affine + AggregatePublicKey = blst.P1Aggregate +) + +// PublicKeyToBytes returns the compressed big-endian format of the public key. +func PublicKeyToBytes(pk *PublicKey) []byte { + return pk.Compress() +} + +// PublicKeyFromBytes parses the compressed big-endian format of the public key +// into a public key. +func PublicKeyFromBytes(pkBytes []byte) (*PublicKey, error) { + pk := new(PublicKey).Uncompress(pkBytes) + if pk == nil { + return nil, errFailedPublicKeyDecompress + } + if !pk.KeyValidate() { + return nil, errInvalidPublicKey + } + return pk, nil +} + +// AggregatePublicKeys aggregates a non-zero number of public keys into a single +// aggregated public key. +// Invariant: all [pks] have been validated. +func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) { + if len(pks) == 0 { + return nil, errNoPublicKeys + } + + var agg AggregatePublicKey + if !agg.Aggregate(pks, false) { + return nil, errFailedPublicKeyAggregation + } + return agg.ToAffine(), nil +} + +// Verify the [sig] of [msg] against the [pk]. +// The [sig] and [pk] may have been an aggregation of other signatures and keys. +// Invariant: [pk] and [sig] have both been validated. +func Verify(pk *PublicKey, sig *Signature, msg []byte) bool { + return sig.Verify(false, pk, false, msg, ciphersuiteSignature) +} + +// Verify the possession of the secret pre-image of [sk] by verifying a [sig] of +// [msg] against the [pk]. +// The [sig] and [pk] may have been an aggregation of other signatures and keys. +// Invariant: [pk] and [sig] have both been validated. +func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool { + return sig.Verify(false, pk, false, msg, ciphersuiteProofOfPossession) +} diff --git a/avalanchego/utils/crypto/bls/public_test.go b/avalanchego/utils/crypto/bls/public_test.go new file mode 100644 index 00000000..c4b0b54c --- /dev/null +++ b/avalanchego/utils/crypto/bls/public_test.go @@ -0,0 +1,56 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package bls + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/utils" +) + +func TestPublicKeyFromBytesWrongSize(t *testing.T) { + require := require.New(t) + + pkBytes := utils.RandomBytes(PublicKeyLen + 1) + _, err := PublicKeyFromBytes(pkBytes) + require.ErrorIs(err, errFailedPublicKeyDecompress) +} + +func TestPublicKeyBytes(t *testing.T) { + require := require.New(t) + + sk, err := NewSecretKey() + require.NoError(err) + + pk := PublicFromSecretKey(sk) + pkBytes := PublicKeyToBytes(pk) + + pk2, err := PublicKeyFromBytes(pkBytes) + require.NoError(err) + pk2Bytes := PublicKeyToBytes(pk2) + + require.Equal(pk, pk2) + require.Equal(pkBytes, pk2Bytes) +} + +func TestAggregatePublicKeysNoop(t *testing.T) { + require := require.New(t) + + sk, err := NewSecretKey() + require.NoError(err) + + pk := PublicFromSecretKey(sk) + pkBytes := PublicKeyToBytes(pk) + + aggPK, err := AggregatePublicKeys([]*PublicKey{pk}) + require.NoError(err) + + aggPKBytes := PublicKeyToBytes(aggPK) + require.NoError(err) + + require.Equal(pk, aggPK) + require.Equal(pkBytes, aggPKBytes) +} diff --git a/avalanchego/utils/crypto/bls/secret.go b/avalanchego/utils/crypto/bls/secret.go new file mode 100644 index 00000000..382f043b --- /dev/null +++ b/avalanchego/utils/crypto/bls/secret.go @@ -0,0 +1,64 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package bls + +import ( + "crypto/rand" + "errors" + + blst "github.com/supranational/blst/bindings/go" +) + +const SecretKeyLen = blst.BLST_SCALAR_BYTES + +var ( + errFailedSecretKeyDeserialize = errors.New("couldn't deserialize secret key") + + // The ciphersuite is more commonly known as G2ProofOfPossession. + // There are two digests to ensure that that message space for normal + // signatures and the proof of possession are distinct. + ciphersuiteSignature = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_") + ciphersuiteProofOfPossession = []byte("BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_") +) + +type SecretKey = blst.SecretKey + +// NewSecretKey generates a new secret key from the local source of +// cryptographically secure randomness. +func NewSecretKey() (*SecretKey, error) { + var ikm [32]byte + _, err := rand.Read(ikm[:]) + return blst.KeyGen(ikm[:]), err +} + +// SecretKeyToBytes returns the big-endian format of the secret key. +func SecretKeyToBytes(sk *SecretKey) []byte { + return sk.Serialize() +} + +// SecretKeyFromBytes parses the big-endian format of the secret key into a +// secret key. +func SecretKeyFromBytes(skBytes []byte) (*SecretKey, error) { + sk := new(SecretKey).Deserialize(skBytes) + if sk == nil { + return nil, errFailedSecretKeyDeserialize + } + return sk, nil +} + +// PublicFromSecretKey returns the public key that corresponds to this secret +// key. +func PublicFromSecretKey(sk *SecretKey) *PublicKey { + return new(PublicKey).From(sk) +} + +// Sign [msg] to authorize this message from this [sk]. +func Sign(sk *SecretKey, msg []byte) *Signature { + return new(Signature).Sign(sk, msg, ciphersuiteSignature) +} + +// Sign [msg] to prove the ownership of this [sk]. +func SignProofOfPossession(sk *SecretKey, msg []byte) *Signature { + return new(Signature).Sign(sk, msg, ciphersuiteProofOfPossession) +} diff --git a/avalanchego/utils/crypto/bls/secret_test.go b/avalanchego/utils/crypto/bls/secret_test.go new file mode 100644 index 00000000..0968f6b1 --- /dev/null +++ b/avalanchego/utils/crypto/bls/secret_test.go @@ -0,0 +1,49 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package bls + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/utils" +) + +func TestSecretKeyFromBytesZero(t *testing.T) { + require := require.New(t) + + var skArr [SecretKeyLen]byte + skBytes := skArr[:] + _, err := SecretKeyFromBytes(skBytes) + require.ErrorIs(err, errFailedSecretKeyDeserialize) +} + +func TestSecretKeyFromBytesWrongSize(t *testing.T) { + require := require.New(t) + + skBytes := utils.RandomBytes(SecretKeyLen + 1) + _, err := SecretKeyFromBytes(skBytes) + require.ErrorIs(err, errFailedSecretKeyDeserialize) +} + +func TestSecretKeyBytes(t *testing.T) { + require := require.New(t) + + msg := utils.RandomBytes(1234) + + sk, err := NewSecretKey() + require.NoError(err) + sig := Sign(sk, msg) + skBytes := SecretKeyToBytes(sk) + + sk2, err := SecretKeyFromBytes(skBytes) + require.NoError(err) + sig2 := Sign(sk2, msg) + sk2Bytes := SecretKeyToBytes(sk2) + + require.Equal(sk, sk2) + require.Equal(skBytes, sk2Bytes) + require.Equal(sig, sig2) +} diff --git a/avalanchego/utils/crypto/bls/signature.go b/avalanchego/utils/crypto/bls/signature.go new file mode 100644 index 00000000..42f1b500 --- /dev/null +++ b/avalanchego/utils/crypto/bls/signature.go @@ -0,0 +1,57 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package bls + +import ( + "errors" + + blst "github.com/supranational/blst/bindings/go" +) + +const SignatureLen = blst.BLST_P2_COMPRESS_BYTES + +var ( + errFailedSignatureDecompress = errors.New("couldn't decompress signature") + errInvalidSignature = errors.New("invalid signature") + errNoSignatures = errors.New("no signatures") + errFailedSignatureAggregation = errors.New("couldn't aggregate signatures") +) + +type ( + Signature = blst.P2Affine + AggregateSignature = blst.P2Aggregate +) + +// SignatureToBytes returns the compressed big-endian format of the signature. +func SignatureToBytes(sig *Signature) []byte { + return sig.Compress() +} + +// SignatureFromBytes parses the compressed big-endian format of the signature +// into a signature. +func SignatureFromBytes(sigBytes []byte) (*Signature, error) { + sig := new(Signature).Uncompress(sigBytes) + if sig == nil { + return nil, errFailedSignatureDecompress + } + if !sig.SigValidate(false) { + return nil, errInvalidSignature + } + return sig, nil +} + +// AggregateSignatures aggregates a non-zero number of signatures into a single +// aggregated signature. +// Invariant: all [sigs] have been validated. +func AggregateSignatures(sigs []*Signature) (*Signature, error) { + if len(sigs) == 0 { + return nil, errNoSignatures + } + + var agg AggregateSignature + if !agg.Aggregate(sigs, false) { + return nil, errFailedSignatureAggregation + } + return agg.ToAffine(), nil +} diff --git a/avalanchego/utils/crypto/bls/signature_test.go b/avalanchego/utils/crypto/bls/signature_test.go new file mode 100644 index 00000000..97ad9837 --- /dev/null +++ b/avalanchego/utils/crypto/bls/signature_test.go @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package bls + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/utils" +) + +func TestSignatureBytes(t *testing.T) { + require := require.New(t) + + msg := utils.RandomBytes(1234) + + sk, err := NewSecretKey() + require.NoError(err) + sig := Sign(sk, msg) + sigBytes := SignatureToBytes(sig) + + sig2, err := SignatureFromBytes(sigBytes) + require.NoError(err) + sig2Bytes := SignatureToBytes(sig2) + + require.Equal(sig, sig2) + require.Equal(sigBytes, sig2Bytes) +} + +func TestAggregateSignaturesNoop(t *testing.T) { + require := require.New(t) + + msg := utils.RandomBytes(1234) + + sk, err := NewSecretKey() + require.NoError(err) + + sig := Sign(sk, msg) + sigBytes := SignatureToBytes(sig) + + aggSig, err := AggregateSignatures([]*Signature{sig}) + require.NoError(err) + + aggSigBytes := SignatureToBytes(aggSig) + require.NoError(err) + + require.Equal(sig, aggSig) + require.Equal(sigBytes, aggSigBytes) +} diff --git a/avalanchego/utils/crypto/crypto.go b/avalanchego/utils/crypto/crypto.go index 21d89199..a5713c5a 100644 --- a/avalanchego/utils/crypto/crypto.go +++ b/avalanchego/utils/crypto/crypto.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/crypto_benchmark_test.go b/avalanchego/utils/crypto/crypto_benchmark_test.go index 90accc7b..dd2d6376 100644 --- a/avalanchego/utils/crypto/crypto_benchmark_test.go +++ b/avalanchego/utils/crypto/crypto_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/ed25519.go b/avalanchego/utils/crypto/ed25519.go index 460e5212..5b3458d7 100644 --- a/avalanchego/utils/crypto/ed25519.go +++ b/avalanchego/utils/crypto/ed25519.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/errors.go b/avalanchego/utils/crypto/errors.go index f2fb3987..ad9b5ebe 100644 --- a/avalanchego/utils/crypto/errors.go +++ b/avalanchego/utils/crypto/errors.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/rsa.go b/avalanchego/utils/crypto/rsa.go index 9564bcda..cd085fcb 100644 --- a/avalanchego/utils/crypto/rsa.go +++ b/avalanchego/utils/crypto/rsa.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/rsapss.go b/avalanchego/utils/crypto/rsapss.go index 050c539f..d0ae67be 100644 --- a/avalanchego/utils/crypto/rsapss.go +++ b/avalanchego/utils/crypto/rsapss.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/secp256k1r.go b/avalanchego/utils/crypto/secp256k1r.go index 2f57637b..ad0cd33f 100644 --- a/avalanchego/utils/crypto/secp256k1r.go +++ b/avalanchego/utils/crypto/secp256k1r.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/secp256k1r_test.go b/avalanchego/utils/crypto/secp256k1r_test.go index 9774a0c3..0c4fc435 100644 --- a/avalanchego/utils/crypto/secp256k1r_test.go +++ b/avalanchego/utils/crypto/secp256k1r_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/crypto/test_keys.go b/avalanchego/utils/crypto/test_keys.go index 05893e6d..359779a4 100644 --- a/avalanchego/utils/crypto/test_keys.go +++ b/avalanchego/utils/crypto/test_keys.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package crypto diff --git a/avalanchego/utils/filesystem/io.go b/avalanchego/utils/filesystem/io.go index 5467bfe8..659c30d0 100644 --- a/avalanchego/utils/filesystem/io.go +++ b/avalanchego/utils/filesystem/io.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package filesystem diff --git a/avalanchego/utils/filesystem/mock_file.go b/avalanchego/utils/filesystem/mock_file.go index 23eeec9c..a9295ec0 100644 --- a/avalanchego/utils/filesystem/mock_file.go +++ b/avalanchego/utils/filesystem/mock_file.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package filesystem diff --git a/avalanchego/utils/formatting/address/address.go b/avalanchego/utils/formatting/address/address.go index cd010457..eaebd510 100644 --- a/avalanchego/utils/formatting/address/address.go +++ b/avalanchego/utils/formatting/address/address.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package address diff --git a/avalanchego/utils/formatting/address/converter.go b/avalanchego/utils/formatting/address/converter.go index 950fea35..5be67e31 100644 --- a/avalanchego/utils/formatting/address/converter.go +++ b/avalanchego/utils/formatting/address/converter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package address diff --git a/avalanchego/utils/formatting/address/tools.go b/avalanchego/utils/formatting/address/tools.go index 7358114d..48c6b0ca 100644 --- a/avalanchego/utils/formatting/address/tools.go +++ b/avalanchego/utils/formatting/address/tools.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. //go:build tools diff --git a/avalanchego/utils/formatting/encoding.go b/avalanchego/utils/formatting/encoding.go index 45cdcbf1..65bf643e 100644 --- a/avalanchego/utils/formatting/encoding.go +++ b/avalanchego/utils/formatting/encoding.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package formatting diff --git a/avalanchego/utils/formatting/encoding_benchmark_test.go b/avalanchego/utils/formatting/encoding_benchmark_test.go index 32bbd581..09cd2f95 100644 --- a/avalanchego/utils/formatting/encoding_benchmark_test.go +++ b/avalanchego/utils/formatting/encoding_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package formatting diff --git a/avalanchego/utils/formatting/encoding_test.go b/avalanchego/utils/formatting/encoding_test.go index 304be4cd..c6382ab8 100644 --- a/avalanchego/utils/formatting/encoding_test.go +++ b/avalanchego/utils/formatting/encoding_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package formatting diff --git a/avalanchego/utils/formatting/int_format.go b/avalanchego/utils/formatting/int_format.go index 89bc8a35..c927f149 100644 --- a/avalanchego/utils/formatting/int_format.go +++ b/avalanchego/utils/formatting/int_format.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package formatting diff --git a/avalanchego/utils/formatting/int_format_test.go b/avalanchego/utils/formatting/int_format_test.go index 311da432..596ec9ac 100644 --- a/avalanchego/utils/formatting/int_format_test.go +++ b/avalanchego/utils/formatting/int_format_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package formatting diff --git a/avalanchego/utils/formatting/prefixed_stringer.go b/avalanchego/utils/formatting/prefixed_stringer.go index 5a02921c..b1b0287c 100644 --- a/avalanchego/utils/formatting/prefixed_stringer.go +++ b/avalanchego/utils/formatting/prefixed_stringer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package formatting diff --git a/avalanchego/utils/hashing/consistent/hashable.go b/avalanchego/utils/hashing/consistent/hashable.go index 07bcf3c0..bb119b35 100644 --- a/avalanchego/utils/hashing/consistent/hashable.go +++ b/avalanchego/utils/hashing/consistent/hashable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package consistent diff --git a/avalanchego/utils/hashing/consistent/ring.go b/avalanchego/utils/hashing/consistent/ring.go index b952797a..8f5e32e5 100644 --- a/avalanchego/utils/hashing/consistent/ring.go +++ b/avalanchego/utils/hashing/consistent/ring.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package consistent diff --git a/avalanchego/utils/hashing/consistent/ring_test.go b/avalanchego/utils/hashing/consistent/ring_test.go index 4e4f9506..af84680a 100644 --- a/avalanchego/utils/hashing/consistent/ring_test.go +++ b/avalanchego/utils/hashing/consistent/ring_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package consistent diff --git a/avalanchego/utils/hashing/hasher.go b/avalanchego/utils/hashing/hasher.go index 2d027374..348daa10 100644 --- a/avalanchego/utils/hashing/hasher.go +++ b/avalanchego/utils/hashing/hasher.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package hashing diff --git a/avalanchego/utils/hashing/hashing.go b/avalanchego/utils/hashing/hashing.go index 950ad2a9..fdb9a0b3 100644 --- a/avalanchego/utils/hashing/hashing.go +++ b/avalanchego/utils/hashing/hashing.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package hashing diff --git a/avalanchego/utils/ips/claimed_ip_port.go b/avalanchego/utils/ips/claimed_ip_port.go index 421957d4..2d6c055c 100644 --- a/avalanchego/utils/ips/claimed_ip_port.go +++ b/avalanchego/utils/ips/claimed_ip_port.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ips diff --git a/avalanchego/utils/ips/dynamic_ip_port.go b/avalanchego/utils/ips/dynamic_ip_port.go index 93a933b3..6b3a4760 100644 --- a/avalanchego/utils/ips/dynamic_ip_port.go +++ b/avalanchego/utils/ips/dynamic_ip_port.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ips diff --git a/avalanchego/utils/ips/ip_port.go b/avalanchego/utils/ips/ip_port.go index 56c84d73..43b5f80a 100644 --- a/avalanchego/utils/ips/ip_port.go +++ b/avalanchego/utils/ips/ip_port.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ips diff --git a/avalanchego/utils/json/codec.go b/avalanchego/utils/json/codec.go index 2a34954f..420efade 100644 --- a/avalanchego/utils/json/codec.go +++ b/avalanchego/utils/json/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/json/float32.go b/avalanchego/utils/json/float32.go index 4200a4e3..ea583646 100644 --- a/avalanchego/utils/json/float32.go +++ b/avalanchego/utils/json/float32.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/json/float32_test.go b/avalanchego/utils/json/float32_test.go index 29b90f6a..45cc05ad 100644 --- a/avalanchego/utils/json/float32_test.go +++ b/avalanchego/utils/json/float32_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/json/float64.go b/avalanchego/utils/json/float64.go index 2be592be..cdee2622 100644 --- a/avalanchego/utils/json/float64.go +++ b/avalanchego/utils/json/float64.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/json/uint16.go b/avalanchego/utils/json/uint16.go index 017f70f9..a697dc5e 100644 --- a/avalanchego/utils/json/uint16.go +++ b/avalanchego/utils/json/uint16.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/json/uint32.go b/avalanchego/utils/json/uint32.go index 02dad836..b1991aa2 100644 --- a/avalanchego/utils/json/uint32.go +++ b/avalanchego/utils/json/uint32.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/json/uint64.go b/avalanchego/utils/json/uint64.go index 06d59bb6..b2d94969 100644 --- a/avalanchego/utils/json/uint64.go +++ b/avalanchego/utils/json/uint64.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/json/uint8.go b/avalanchego/utils/json/uint8.go index 957e6cc4..e72ac2ae 100644 --- a/avalanchego/utils/json/uint8.go +++ b/avalanchego/utils/json/uint8.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package json diff --git a/avalanchego/utils/linkedhashmap/iterator.go b/avalanchego/utils/linkedhashmap/iterator.go new file mode 100644 index 00000000..661e236a --- /dev/null +++ b/avalanchego/utils/linkedhashmap/iterator.go @@ -0,0 +1,70 @@ +// Copyright (C) 2019-2022, Ava Labs, Inte. All rights reserved. +// See the file LICENSE for licensing terms. + +package linkedhashmap + +import ( + "container/list" + + "github.com/ava-labs/avalanchego/utils" +) + +var _ Iter[int, struct{}] = &iterator[int, struct{}]{} + +// Iterates over the keys and values in a LinkedHashmap +// from oldest to newest elements. +// Assumes the underlying LinkedHashmap is not modified while +// the iterator is in use, except to delete elements that +// have already been iterated over. +type Iter[K, V any] interface { + Next() bool + Key() K + Value() V +} + +type iterator[K comparable, V any] struct { + lh *linkedHashmap[K, V] + key K + value V + next *list.Element + initialized, exhausted bool +} + +func (it *iterator[K, V]) Next() bool { + // If the iterator has been exhausted, there is no next value. + if it.exhausted { + it.key = utils.Zero[K]() + it.value = utils.Zero[V]() + it.next = nil + return false + } + + it.lh.lock.RLock() + defer it.lh.lock.RUnlock() + + // If the iterator was not yet initialized, do it now. + if !it.initialized { + it.initialized = true + oldest := it.lh.entryList.Front() + if oldest == nil { + it.exhausted = true + it.key = utils.Zero[K]() + it.value = utils.Zero[V]() + it.next = nil + return false + } + it.next = oldest + } + + // It's important to ensure that [it.next] is not nil + // by not deleting elements that have not yet been iterated + // over from [it.lh] + it.key = it.next.Value.(keyValue[K, V]).key + it.value = it.next.Value.(keyValue[K, V]).value + it.next = it.next.Next() // Next time, return next element + it.exhausted = it.next == nil + return true +} + +func (it *iterator[K, V]) Key() K { return it.key } +func (it *iterator[K, V]) Value() V { return it.value } diff --git a/avalanchego/utils/linkedhashmap/linkedhashmap.go b/avalanchego/utils/linkedhashmap/linkedhashmap.go index 42da5d47..213d61d5 100644 --- a/avalanchego/utils/linkedhashmap/linkedhashmap.go +++ b/avalanchego/utils/linkedhashmap/linkedhashmap.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inte. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inte. All rights reserved. // See the file LICENSE for licensing terms. package linkedhashmap @@ -7,191 +7,135 @@ import ( "container/list" "sync" - "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils" ) +var _ LinkedHashmap[int, struct{}] = &linkedHashmap[int, struct{}]{} + // Hashmap provides an O(1) mapping from a comparable key to any value. // Comparable is defined by https://golang.org/ref/spec#Comparison_operators. -type Hashmap interface { - Put(key, val interface{}) - Get(key interface{}) (val interface{}, exists bool) - Delete(key interface{}) +type Hashmap[K, V any] interface { + Put(key K, val V) + Get(key K) (val V, exists bool) + Delete(key K) Len() int } // LinkedHashmap is a hashmap that keeps track of the oldest pairing an the // newest pairing. -type LinkedHashmap interface { - Hashmap - - Oldest() (key interface{}, val interface{}, exists bool) - Newest() (key interface{}, val interface{}, exists bool) - NewIterator() Iter -} +type LinkedHashmap[K, V any] interface { + Hashmap[K, V] -// Iterates over the keys and values in a LinkedHashmap -// from oldest to newest elements. -// Assumes the underlying LinkedHashmap is not modified while -// the iterator is in use, except to delete elements that -// have already been iterated over. -type Iter interface { - Next() bool - Key() interface{} - Value() interface{} + Oldest() (key K, val V, exists bool) + Newest() (key K, val V, exists bool) + NewIterator() Iter[K, V] } -type keyValue struct { - key interface{} - value interface{} +type keyValue[K, V any] struct { + key K + value V } -type linkedHashmap struct { +type linkedHashmap[K comparable, V any] struct { lock sync.RWMutex - entryMap map[interface{}]*list.Element + entryMap map[K]*list.Element entryList *list.List } -func New() LinkedHashmap { - return &linkedHashmap{ - entryMap: make(map[interface{}]*list.Element), +func New[K comparable, V any]() LinkedHashmap[K, V] { + return &linkedHashmap[K, V]{ + entryMap: make(map[K]*list.Element), entryList: list.New(), } } -func (lh *linkedHashmap) Put(key, val interface{}) { +func (lh *linkedHashmap[K, V]) Put(key K, val V) { lh.lock.Lock() defer lh.lock.Unlock() lh.put(key, val) } -func (lh *linkedHashmap) Get(key interface{}) (interface{}, bool) { +func (lh *linkedHashmap[K, V]) Get(key K) (V, bool) { lh.lock.Lock() defer lh.lock.Unlock() return lh.get(key) } -func (lh *linkedHashmap) Delete(key interface{}) { +func (lh *linkedHashmap[K, V]) Delete(key K) { lh.lock.Lock() defer lh.lock.Unlock() lh.delete(key) } -func (lh *linkedHashmap) Len() int { +func (lh *linkedHashmap[K, V]) Len() int { lh.lock.Lock() defer lh.lock.Unlock() return lh.len() } -func (lh *linkedHashmap) Oldest() (interface{}, interface{}, bool) { +func (lh *linkedHashmap[K, V]) Oldest() (K, V, bool) { lh.lock.Lock() defer lh.lock.Unlock() return lh.oldest() } -func (lh *linkedHashmap) Newest() (interface{}, interface{}, bool) { +func (lh *linkedHashmap[K, V]) Newest() (K, V, bool) { lh.lock.Lock() defer lh.lock.Unlock() return lh.newest() } -func (lh *linkedHashmap) put(key, value interface{}) { +func (lh *linkedHashmap[K, V]) put(key K, value V) { if e, ok := lh.entryMap[key]; ok { lh.entryList.MoveToBack(e) - e.Value = keyValue{ + e.Value = keyValue[K, V]{ key: key, value: value, } } else { - lh.entryMap[key] = lh.entryList.PushBack(keyValue{ + lh.entryMap[key] = lh.entryList.PushBack(keyValue[K, V]{ key: key, value: value, }) } } -func (lh *linkedHashmap) get(key interface{}) (interface{}, bool) { +func (lh *linkedHashmap[K, V]) get(key K) (V, bool) { if e, ok := lh.entryMap[key]; ok { - return e.Value.(keyValue).value, true + return e.Value.(keyValue[K, V]).value, true } - return nil, false + return utils.Zero[V](), false } -func (lh *linkedHashmap) delete(key interface{}) { +func (lh *linkedHashmap[K, V]) delete(key K) { if e, ok := lh.entryMap[key]; ok { lh.entryList.Remove(e) delete(lh.entryMap, key) } } -func (lh *linkedHashmap) len() int { return len(lh.entryMap) } +func (lh *linkedHashmap[K, V]) len() int { return len(lh.entryMap) } -func (lh *linkedHashmap) oldest() (interface{}, interface{}, bool) { +func (lh *linkedHashmap[K, V]) oldest() (K, V, bool) { if val := lh.entryList.Front(); val != nil { - return val.Value.(keyValue).key, val.Value.(keyValue).value, true + return val.Value.(keyValue[K, V]).key, val.Value.(keyValue[K, V]).value, true } - return nil, nil, false + return utils.Zero[K](), utils.Zero[V](), false } -func (lh *linkedHashmap) newest() (interface{}, interface{}, bool) { +func (lh *linkedHashmap[K, V]) newest() (K, V, bool) { if val := lh.entryList.Back(); val != nil { - return val.Value.(keyValue).key, val.Value.(keyValue).value, true + return val.Value.(keyValue[K, V]).key, val.Value.(keyValue[K, V]).value, true } - return nil, nil, false + return utils.Zero[K](), utils.Zero[V](), false } -func (lh *linkedHashmap) NewIterator() Iter { - return &iterator{lh: lh} +func (lh *linkedHashmap[K, V]) NewIterator() Iter[K, V] { + return &iterator[K, V]{lh: lh} } - -type iterator struct { - lh *linkedHashmap - key interface{} - value interface{} - next *list.Element - initialized, exhausted bool -} - -func (it *iterator) Next() bool { - // If the iterator has been exhausted, there is no next value. - if it.exhausted { - it.key = ids.Empty - it.value = nil - it.next = nil - return false - } - - it.lh.lock.RLock() - defer it.lh.lock.RUnlock() - - // If the iterator was not yet initialized, do it now. - if !it.initialized { - it.initialized = true - oldest := it.lh.entryList.Front() - if oldest == nil { - it.exhausted = true - it.key = ids.Empty - it.value = nil - it.next = nil - return false - } - it.next = oldest - } - - // It's important to ensure that [it.next] is not nil - // by not deleting elements that have not yet been iterated - // over from [it.lh] - it.key = it.next.Value.(keyValue).key - it.value = it.next.Value.(keyValue).value - it.next = it.next.Next() // Next time, return next element - it.exhausted = it.next == nil - return true -} - -func (it *iterator) Key() interface{} { return it.key } -func (it *iterator) Value() interface{} { return it.value } diff --git a/avalanchego/utils/linkedhashmap/linkedhashmap_test.go b/avalanchego/utils/linkedhashmap/linkedhashmap_test.go index 66ef7757..977e0a91 100644 --- a/avalanchego/utils/linkedhashmap/linkedhashmap_test.go +++ b/avalanchego/utils/linkedhashmap/linkedhashmap_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package linkedhashmap @@ -14,7 +14,7 @@ import ( func TestLinkedHashmap(t *testing.T) { require := require.New(t) - lh := New() + lh := New[ids.ID, int]() require.Equal(0, lh.Len(), "a new hashmap should be empty") key0 := ids.GenerateTestID() @@ -101,7 +101,7 @@ func TestIterator(t *testing.T) { // Case: No elements { - lh := New() + lh := New[ids.ID, int]() iter := lh.NewIterator() require.NotNil(iter) // Should immediately be exhausted @@ -109,12 +109,12 @@ func TestIterator(t *testing.T) { require.False(iter.Next()) // Should be empty require.EqualValues(ids.Empty, iter.Key()) - require.Nil(iter.Value()) + require.Zero(iter.Value()) } // Case: 1 element { - lh := New() + lh := New[ids.ID, int]() iter := lh.NewIterator() require.NotNil(iter) lh.Put(id1, 1) @@ -141,7 +141,7 @@ func TestIterator(t *testing.T) { // Case: Multiple elements { - lh := New() + lh := New[ids.ID, int]() lh.Put(id1, 1) lh.Put(id2, 2) lh.Put(id3, 3) @@ -162,7 +162,7 @@ func TestIterator(t *testing.T) { // Case: Delete element that has been iterated over { - lh := New() + lh := New[ids.ID, int]() lh.Put(id1, 1) lh.Put(id2, 2) lh.Put(id3, 3) diff --git a/avalanchego/utils/logging/color.go b/avalanchego/utils/logging/color.go index 1cd969ba..072f85b2 100644 --- a/avalanchego/utils/logging/color.go +++ b/avalanchego/utils/logging/color.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging diff --git a/avalanchego/utils/logging/config.go b/avalanchego/utils/logging/config.go index d3ab299c..afc2b480 100644 --- a/avalanchego/utils/logging/config.go +++ b/avalanchego/utils/logging/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging @@ -15,7 +15,6 @@ type RotatingWriterConfig struct { type Config struct { RotatingWriterConfig DisableWriterDisplaying bool `json:"disableWriterDisplaying"` - Assertions bool `json:"assertions"` LogLevel Level `json:"logLevel"` DisplayLevel Level `json:"displayLevel"` LogFormat Format `json:"logFormat"` diff --git a/avalanchego/utils/logging/factory.go b/avalanchego/utils/logging/factory.go index cf5eeb60..f4421239 100644 --- a/avalanchego/utils/logging/factory.go +++ b/avalanchego/utils/logging/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging @@ -89,7 +89,7 @@ func (f *factory) makeLogger(config Config) (Logger, error) { fileCore := NewWrappedCore(config.LogLevel, rw, fileEnc) prefix := config.LogFormat.WrapPrefix(config.MsgPrefix) - l := NewLogger(config.Assertions, prefix, consoleCore, fileCore) + l := NewLogger(prefix, consoleCore, fileCore) f.loggers[config.LoggerName] = logWrapper{ logger: l, displayLevel: consoleCore.AtomicLevel, diff --git a/avalanchego/utils/logging/level.go b/avalanchego/utils/logging/level.go index 0e809869..b2e58191 100644 --- a/avalanchego/utils/logging/level.go +++ b/avalanchego/utils/logging/level.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging diff --git a/avalanchego/utils/logging/log.go b/avalanchego/utils/logging/log.go index 23c1b52c..8975cd13 100644 --- a/avalanchego/utils/logging/log.go +++ b/avalanchego/utils/logging/log.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging @@ -13,9 +13,8 @@ import ( var _ Logger = &log{} type log struct { - assertionsEnabled bool - wrappedCores []WrappedCore - internalLogger *zap.Logger + wrappedCores []WrappedCore + internalLogger *zap.Logger } type WrappedCore struct { @@ -47,11 +46,10 @@ func newZapLogger(prefix string, wrappedCores ...WrappedCore) *zap.Logger { } // New returns a new logger set up according to [config] -func NewLogger(assertionsEnabled bool, prefix string, wrappedCores ...WrappedCore) Logger { +func NewLogger(prefix string, wrappedCores ...WrappedCore) Logger { return &log{ - assertionsEnabled: assertionsEnabled, - internalLogger: newZapLogger(prefix, wrappedCores...), - wrappedCores: wrappedCores, + internalLogger: newZapLogger(prefix, wrappedCores...), + wrappedCores: wrappedCores, } } @@ -106,43 +104,6 @@ func (l *log) Verbo(msg string, fields ...zap.Field) { l.log(Verbo, msg, fields...) } -func (l *log) AssertNoError(err error) { - if err != nil { - l.Fatal("assertion failed", zap.Error(err)) - } - if l.assertionsEnabled && err != nil { - l.Stop() - panic(err) - } -} - -func (l *log) AssertTrue(b bool, msg string, fields ...zap.Field) { - if !b { - l.Fatal(msg, fields...) - } - if l.assertionsEnabled && !b { - l.Stop() - panic(msg) - } -} - -func (l *log) AssertDeferredTrue(f func() bool, msg string, fields ...zap.Field) { - // Note, the logger will only be notified here if assertions are enabled - if l.assertionsEnabled && !f() { - l.Fatal(msg, fields...) - l.Stop() - panic(msg) - } -} - -func (l *log) AssertDeferredNoError(f func() error) { - if l.assertionsEnabled { - if err := f(); err != nil { - l.AssertNoError(err) - } - } -} - func (l *log) StopOnPanic() { if r := recover(); r != nil { l.Fatal("panicking", zap.Any("reason", r), zap.Stack("from")) diff --git a/avalanchego/utils/logging/log_test.go b/avalanchego/utils/logging/log_test.go index 234e7a74..be8cd607 100644 --- a/avalanchego/utils/logging/log_test.go +++ b/avalanchego/utils/logging/log_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging @@ -6,7 +6,7 @@ package logging import "testing" func TestLog(t *testing.T) { - log := NewLogger(false, "", NewWrappedCore(Info, Discard, Plain.ConsoleEncoder())) + log := NewLogger("", NewWrappedCore(Info, Discard, Plain.ConsoleEncoder())) recovered := new(bool) panicFunc := func() { diff --git a/avalanchego/utils/logging/logger.go b/avalanchego/utils/logging/logger.go index 6934c5b9..8bccc27f 100644 --- a/avalanchego/utils/logging/logger.go +++ b/avalanchego/utils/logging/logger.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging @@ -36,17 +36,6 @@ type Logger interface { // aspect of the program Verbo(msg string, fields ...zap.Field) - // If assertions are enabled, will result in a panic if err is non-nil - AssertNoError(err error) - // If assertions are enabled, will result in a panic if b is false - AssertTrue(b bool, msg string, fields ...zap.Field) - // If assertions are enabled, the function will be called and will result in - // a panic the returned value is non-nil - AssertDeferredNoError(f func() error) - // If assertions are enabled, the function will be called and will result in - // a panic the returned value is false - AssertDeferredTrue(f func() bool, msg string, fields ...zap.Field) - // Recovers a panic, logs the error, and rethrows the panic. StopOnPanic() // If a function panics, this will log that panic and then re-panic ensuring diff --git a/avalanchego/utils/logging/mock_logger.go b/avalanchego/utils/logging/mock_logger.go index 68986a90..fa93ca1f 100644 --- a/avalanchego/utils/logging/mock_logger.go +++ b/avalanchego/utils/logging/mock_logger.go @@ -34,64 +34,6 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { return m.recorder } -// AssertDeferredNoError mocks base method. -func (m *MockLogger) AssertDeferredNoError(f func() error) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AssertDeferredNoError", f) -} - -// AssertDeferredNoError indicates an expected call of AssertDeferredNoError. -func (mr *MockLoggerMockRecorder) AssertDeferredNoError(f interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssertDeferredNoError", reflect.TypeOf((*MockLogger)(nil).AssertDeferredNoError), f) -} - -// AssertDeferredTrue mocks base method. -func (m *MockLogger) AssertDeferredTrue(f func() bool, format string, args ...zap.Field) { - m.ctrl.T.Helper() - varargs := []interface{}{f, format} - for _, a := range args { - varargs = append(varargs, a) - } - m.ctrl.Call(m, "AssertDeferredTrue", varargs...) -} - -// AssertDeferredTrue indicates an expected call of AssertDeferredTrue. -func (mr *MockLoggerMockRecorder) AssertDeferredTrue(f, format interface{}, args ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{f, format}, args...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssertDeferredTrue", reflect.TypeOf((*MockLogger)(nil).AssertDeferredTrue), varargs...) -} - -// AssertNoError mocks base method. -func (m *MockLogger) AssertNoError(err error) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AssertNoError", err) -} - -// AssertNoError indicates an expected call of AssertNoError. -func (mr *MockLoggerMockRecorder) AssertNoError(err interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssertNoError", reflect.TypeOf((*MockLogger)(nil).AssertNoError), err) -} - -// AssertTrue mocks base method. -func (m *MockLogger) AssertTrue(b bool, format string, args ...zap.Field) { - m.ctrl.T.Helper() - varargs := []interface{}{b, format} - for _, a := range args { - varargs = append(varargs, a) - } - m.ctrl.Call(m, "AssertTrue", varargs...) -} - -// AssertTrue indicates an expected call of AssertTrue. -func (mr *MockLoggerMockRecorder) AssertTrue(b, format interface{}, args ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{b, format}, args...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssertTrue", reflect.TypeOf((*MockLogger)(nil).AssertTrue), varargs...) -} - // Debug mocks base method. func (m *MockLogger) Debug(format string, args ...zap.Field) { m.ctrl.T.Helper() diff --git a/avalanchego/utils/logging/sanitize.go b/avalanchego/utils/logging/sanitize.go index 9028f84d..ab65127e 100644 --- a/avalanchego/utils/logging/sanitize.go +++ b/avalanchego/utils/logging/sanitize.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging diff --git a/avalanchego/utils/logging/test_log.go b/avalanchego/utils/logging/test_log.go index c5fc4480..469a47fe 100644 --- a/avalanchego/utils/logging/test_log.go +++ b/avalanchego/utils/logging/test_log.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package logging @@ -37,14 +37,6 @@ func (NoLog) Debug(string, ...zap.Field) {} func (NoLog) Verbo(string, ...zap.Field) {} -func (NoLog) AssertNoError(error) {} - -func (NoLog) AssertTrue(bool, string, ...zap.Field) {} - -func (NoLog) AssertDeferredTrue(func() bool, string, ...zap.Field) {} - -func (NoLog) AssertDeferredNoError(func() error) {} - func (NoLog) StopOnPanic() {} func (NoLog) RecoverAndPanic(f func()) { f() } diff --git a/avalanchego/utils/math/averager.go b/avalanchego/utils/math/averager.go index 33409787..cdbb4216 100644 --- a/avalanchego/utils/math/averager.go +++ b/avalanchego/utils/math/averager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/math/averager_heap.go b/avalanchego/utils/math/averager_heap.go index daec6e14..d90936ea 100644 --- a/avalanchego/utils/math/averager_heap.go +++ b/avalanchego/utils/math/averager_heap.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math @@ -136,6 +136,7 @@ func (h *averagerHeapBackend) Push(x interface{}) { func (h *averagerHeapBackend) Pop() interface{} { newLen := len(h.entries) - 1 e := h.entries[newLen] + h.entries[newLen] = nil delete(h.nodeIDToEntry, e.nodeID) h.entries = h.entries[:newLen] return e diff --git a/avalanchego/utils/math/averager_heap_test.go b/avalanchego/utils/math/averager_heap_test.go index e7358029..414f2d39 100644 --- a/avalanchego/utils/math/averager_heap_test.go +++ b/avalanchego/utils/math/averager_heap_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/math/continuous_averager.go b/avalanchego/utils/math/continuous_averager.go index 1e78b5ff..43e14a48 100644 --- a/avalanchego/utils/math/continuous_averager.go +++ b/avalanchego/utils/math/continuous_averager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/math/continuous_averager_benchmark_test.go b/avalanchego/utils/math/continuous_averager_benchmark_test.go index 200362c6..ccfbd765 100644 --- a/avalanchego/utils/math/continuous_averager_benchmark_test.go +++ b/avalanchego/utils/math/continuous_averager_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/math/continuous_averager_test.go b/avalanchego/utils/math/continuous_averager_test.go index f873a8ec..a6cffbdb 100644 --- a/avalanchego/utils/math/continuous_averager_test.go +++ b/avalanchego/utils/math/continuous_averager_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/math/meter/continuous_meter.go b/avalanchego/utils/math/meter/continuous_meter.go index 941ec6b8..1c60ea73 100644 --- a/avalanchego/utils/math/meter/continuous_meter.go +++ b/avalanchego/utils/math/meter/continuous_meter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meter diff --git a/avalanchego/utils/math/meter/factory.go b/avalanchego/utils/math/meter/factory.go index 61e97369..ffddc268 100644 --- a/avalanchego/utils/math/meter/factory.go +++ b/avalanchego/utils/math/meter/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meter diff --git a/avalanchego/utils/math/meter/meter.go b/avalanchego/utils/math/meter/meter.go index f0d21337..e185fb16 100644 --- a/avalanchego/utils/math/meter/meter.go +++ b/avalanchego/utils/math/meter/meter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meter diff --git a/avalanchego/utils/math/meter/meter_benchmark_test.go b/avalanchego/utils/math/meter/meter_benchmark_test.go index afa48cc2..7c0c6435 100644 --- a/avalanchego/utils/math/meter/meter_benchmark_test.go +++ b/avalanchego/utils/math/meter/meter_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meter diff --git a/avalanchego/utils/math/meter/meter_test.go b/avalanchego/utils/math/meter/meter_test.go index d623308e..e8dc0c50 100644 --- a/avalanchego/utils/math/meter/meter_test.go +++ b/avalanchego/utils/math/meter/meter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package meter diff --git a/avalanchego/utils/math/safe_math.go b/avalanchego/utils/math/safe_math.go index 17fc4eed..dca38f6b 100644 --- a/avalanchego/utils/math/safe_math.go +++ b/avalanchego/utils/math/safe_math.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/math/safe_math_test.go b/avalanchego/utils/math/safe_math_test.go index 9a8f6a68..4eb6944a 100644 --- a/avalanchego/utils/math/safe_math_test.go +++ b/avalanchego/utils/math/safe_math_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/math/sync_averager.go b/avalanchego/utils/math/sync_averager.go index 9c49a6d9..6858ba76 100644 --- a/avalanchego/utils/math/sync_averager.go +++ b/avalanchego/utils/math/sync_averager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package math diff --git a/avalanchego/utils/metric/api_interceptor.go b/avalanchego/utils/metric/api_interceptor.go index e13cec8d..21e7f7f2 100644 --- a/avalanchego/utils/metric/api_interceptor.go +++ b/avalanchego/utils/metric/api_interceptor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metric diff --git a/avalanchego/utils/metric/averager.go b/avalanchego/utils/metric/averager.go index 29fbd5ff..dec17d69 100644 --- a/avalanchego/utils/metric/averager.go +++ b/avalanchego/utils/metric/averager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metric diff --git a/avalanchego/utils/password/hash.go b/avalanchego/utils/password/hash.go index 8c0e6e16..5ebbc9c3 100644 --- a/avalanchego/utils/password/hash.go +++ b/avalanchego/utils/password/hash.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package password diff --git a/avalanchego/utils/password/hash_test.go b/avalanchego/utils/password/hash_test.go index 58643f06..652f0955 100644 --- a/avalanchego/utils/password/hash_test.go +++ b/avalanchego/utils/password/hash_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package password diff --git a/avalanchego/utils/password/password.go b/avalanchego/utils/password/password.go index 908f7afd..e94c339e 100644 --- a/avalanchego/utils/password/password.go +++ b/avalanchego/utils/password/password.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package password diff --git a/avalanchego/utils/password/password_test.go b/avalanchego/utils/password/password_test.go index 7aebeda9..65c11419 100644 --- a/avalanchego/utils/password/password_test.go +++ b/avalanchego/utils/password/password_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package password diff --git a/avalanchego/utils/perms/chmod.go b/avalanchego/utils/perms/chmod.go index a61261f2..6407fad7 100644 --- a/avalanchego/utils/perms/chmod.go +++ b/avalanchego/utils/perms/chmod.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package perms diff --git a/avalanchego/utils/perms/create.go b/avalanchego/utils/perms/create.go index 170483b9..81110453 100644 --- a/avalanchego/utils/perms/create.go +++ b/avalanchego/utils/perms/create.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package perms diff --git a/avalanchego/utils/perms/perms.go b/avalanchego/utils/perms/perms.go index de552b4a..388b2dc7 100644 --- a/avalanchego/utils/perms/perms.go +++ b/avalanchego/utils/perms/perms.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package perms diff --git a/avalanchego/utils/perms/write_file.go b/avalanchego/utils/perms/write_file.go index e13c2ce8..a1678acf 100644 --- a/avalanchego/utils/perms/write_file.go +++ b/avalanchego/utils/perms/write_file.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package perms diff --git a/avalanchego/utils/profiler/continuous.go b/avalanchego/utils/profiler/continuous.go index 47307c12..05937d52 100644 --- a/avalanchego/utils/profiler/continuous.go +++ b/avalanchego/utils/profiler/continuous.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package profiler diff --git a/avalanchego/utils/profiler/profiler.go b/avalanchego/utils/profiler/profiler.go index 758e7fbd..a4b010c5 100644 --- a/avalanchego/utils/profiler/profiler.go +++ b/avalanchego/utils/profiler/profiler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package profiler diff --git a/avalanchego/utils/profiler/profiler_test.go b/avalanchego/utils/profiler/profiler_test.go index 923b873a..d0f948b9 100644 --- a/avalanchego/utils/profiler/profiler_test.go +++ b/avalanchego/utils/profiler/profiler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package profiler diff --git a/avalanchego/utils/resource/no_usage.go b/avalanchego/utils/resource/no_usage.go index da93f008..e1a74ee8 100644 --- a/avalanchego/utils/resource/no_usage.go +++ b/avalanchego/utils/resource/no_usage.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package resource diff --git a/avalanchego/utils/resource/usage.go b/avalanchego/utils/resource/usage.go index 016e2aa0..d4806b90 100644 --- a/avalanchego/utils/resource/usage.go +++ b/avalanchego/utils/resource/usage.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package resource diff --git a/avalanchego/utils/resource/usage_test.go b/avalanchego/utils/resource/usage_test.go index 4956260a..5c81d62a 100644 --- a/avalanchego/utils/resource/usage_test.go +++ b/avalanchego/utils/resource/usage_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package resource diff --git a/avalanchego/utils/rpc/json.go b/avalanchego/utils/rpc/json.go index c5479047..c87766dd 100644 --- a/avalanchego/utils/rpc/json.go +++ b/avalanchego/utils/rpc/json.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpc diff --git a/avalanchego/utils/rpc/options.go b/avalanchego/utils/rpc/options.go index 68700a8d..a01254fe 100644 --- a/avalanchego/utils/rpc/options.go +++ b/avalanchego/utils/rpc/options.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpc diff --git a/avalanchego/utils/rpc/requester.go b/avalanchego/utils/rpc/requester.go index cf1de0e1..e5671c6d 100644 --- a/avalanchego/utils/rpc/requester.go +++ b/avalanchego/utils/rpc/requester.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpc diff --git a/avalanchego/utils/sampler/rand.go b/avalanchego/utils/sampler/rand.go index c486ecf9..15bb01d7 100644 --- a/avalanchego/utils/sampler/rand.go +++ b/avalanchego/utils/sampler/rand.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/uniform.go b/avalanchego/utils/sampler/uniform.go index 67902256..316e4c9d 100644 --- a/avalanchego/utils/sampler/uniform.go +++ b/avalanchego/utils/sampler/uniform.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/uniform_benchmark_test.go b/avalanchego/utils/sampler/uniform_benchmark_test.go index f8df06cd..9b229bd2 100644 --- a/avalanchego/utils/sampler/uniform_benchmark_test.go +++ b/avalanchego/utils/sampler/uniform_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/uniform_best.go b/avalanchego/utils/sampler/uniform_best.go index 6ec1a475..0492610c 100644 --- a/avalanchego/utils/sampler/uniform_best.go +++ b/avalanchego/utils/sampler/uniform_best.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/uniform_replacer.go b/avalanchego/utils/sampler/uniform_replacer.go index fc2fddc8..45657440 100644 --- a/avalanchego/utils/sampler/uniform_replacer.go +++ b/avalanchego/utils/sampler/uniform_replacer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/uniform_resample.go b/avalanchego/utils/sampler/uniform_resample.go index 101f2a67..ecf3a956 100644 --- a/avalanchego/utils/sampler/uniform_resample.go +++ b/avalanchego/utils/sampler/uniform_resample.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/uniform_test.go b/avalanchego/utils/sampler/uniform_test.go index a03a9a82..02d3fe81 100644 --- a/avalanchego/utils/sampler/uniform_test.go +++ b/avalanchego/utils/sampler/uniform_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted.go b/avalanchego/utils/sampler/weighted.go index 6cdfea9d..24454c32 100644 --- a/avalanchego/utils/sampler/weighted.go +++ b/avalanchego/utils/sampler/weighted.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_array.go b/avalanchego/utils/sampler/weighted_array.go index 852842ad..e6c59d26 100644 --- a/avalanchego/utils/sampler/weighted_array.go +++ b/avalanchego/utils/sampler/weighted_array.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_benchmark_test.go b/avalanchego/utils/sampler/weighted_benchmark_test.go index 9a5f5ed3..2d963503 100644 --- a/avalanchego/utils/sampler/weighted_benchmark_test.go +++ b/avalanchego/utils/sampler/weighted_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_best.go b/avalanchego/utils/sampler/weighted_best.go index b8d4bbbd..b7ba016e 100644 --- a/avalanchego/utils/sampler/weighted_best.go +++ b/avalanchego/utils/sampler/weighted_best.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_heap.go b/avalanchego/utils/sampler/weighted_heap.go index 044894f8..7154eef3 100644 --- a/avalanchego/utils/sampler/weighted_heap.go +++ b/avalanchego/utils/sampler/weighted_heap.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_heap_test.go b/avalanchego/utils/sampler/weighted_heap_test.go index 35bf6a98..ff7f0048 100644 --- a/avalanchego/utils/sampler/weighted_heap_test.go +++ b/avalanchego/utils/sampler/weighted_heap_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_linear.go b/avalanchego/utils/sampler/weighted_linear.go index 3f99edd8..7e5221a6 100644 --- a/avalanchego/utils/sampler/weighted_linear.go +++ b/avalanchego/utils/sampler/weighted_linear.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_test.go b/avalanchego/utils/sampler/weighted_test.go index 158ccb42..a8e6ec0c 100644 --- a/avalanchego/utils/sampler/weighted_test.go +++ b/avalanchego/utils/sampler/weighted_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_uniform.go b/avalanchego/utils/sampler/weighted_uniform.go index cdc8fb9f..0fcfc3c8 100644 --- a/avalanchego/utils/sampler/weighted_uniform.go +++ b/avalanchego/utils/sampler/weighted_uniform.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_without_replacement.go b/avalanchego/utils/sampler/weighted_without_replacement.go index 9f00c847..e162f906 100644 --- a/avalanchego/utils/sampler/weighted_without_replacement.go +++ b/avalanchego/utils/sampler/weighted_without_replacement.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_without_replacement_benchmark_test.go b/avalanchego/utils/sampler/weighted_without_replacement_benchmark_test.go index 17796ff3..01405aa8 100644 --- a/avalanchego/utils/sampler/weighted_without_replacement_benchmark_test.go +++ b/avalanchego/utils/sampler/weighted_without_replacement_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_without_replacement_generic.go b/avalanchego/utils/sampler/weighted_without_replacement_generic.go index 826a10b8..38eeb76b 100644 --- a/avalanchego/utils/sampler/weighted_without_replacement_generic.go +++ b/avalanchego/utils/sampler/weighted_without_replacement_generic.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sampler/weighted_without_replacement_test.go b/avalanchego/utils/sampler/weighted_without_replacement_test.go index 5dca2a6e..8b1bd16b 100644 --- a/avalanchego/utils/sampler/weighted_without_replacement_test.go +++ b/avalanchego/utils/sampler/weighted_without_replacement_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package sampler diff --git a/avalanchego/utils/sorting.go b/avalanchego/utils/sorting.go index 865bf721..4b1e3e53 100644 --- a/avalanchego/utils/sorting.go +++ b/avalanchego/utils/sorting.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utils diff --git a/avalanchego/utils/sorting_test.go b/avalanchego/utils/sorting_test.go index f5a24bdf..85943ae2 100644 --- a/avalanchego/utils/sorting_test.go +++ b/avalanchego/utils/sorting_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utils diff --git a/avalanchego/utils/storage/storage_common.go b/avalanchego/utils/storage/storage_common.go index a20c7ee5..7979ebfa 100644 --- a/avalanchego/utils/storage/storage_common.go +++ b/avalanchego/utils/storage/storage_common.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package storage diff --git a/avalanchego/utils/storage/storage_unix.go b/avalanchego/utils/storage/storage_unix.go index 036880ca..97a24de3 100644 --- a/avalanchego/utils/storage/storage_unix.go +++ b/avalanchego/utils/storage/storage_unix.go @@ -1,7 +1,7 @@ //go:build !windows // +build !windows -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package storage diff --git a/avalanchego/utils/storage/storage_windows.go b/avalanchego/utils/storage/storage_windows.go index aeacee2f..ab70fd96 100644 --- a/avalanchego/utils/storage/storage_windows.go +++ b/avalanchego/utils/storage/storage_windows.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package storage diff --git a/avalanchego/utils/subprocess/linux_new.go b/avalanchego/utils/subprocess/linux_new.go index 52aad156..6b54386b 100644 --- a/avalanchego/utils/subprocess/linux_new.go +++ b/avalanchego/utils/subprocess/linux_new.go @@ -3,7 +3,7 @@ // ^ syscall.SysProcAttr only has field Pdeathsig on Linux -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package subprocess diff --git a/avalanchego/utils/subprocess/non_linux_new.go b/avalanchego/utils/subprocess/non_linux_new.go index e3676848..fabc137c 100644 --- a/avalanchego/utils/subprocess/non_linux_new.go +++ b/avalanchego/utils/subprocess/non_linux_new.go @@ -1,7 +1,7 @@ //go:build !linux // +build !linux -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package subprocess diff --git a/avalanchego/utils/timer/adaptive_timeout_manager.go b/avalanchego/utils/timer/adaptive_timeout_manager.go index 80c3018b..45567659 100644 --- a/avalanchego/utils/timer/adaptive_timeout_manager.go +++ b/avalanchego/utils/timer/adaptive_timeout_manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer @@ -103,6 +103,7 @@ type adaptiveTimeoutManager struct { clock mockable.Clock networkTimeoutMetric, avgLatency prometheus.Gauge numTimeouts prometheus.Counter + numPendingTimeouts prometheus.Gauge // Averages the response time from all peers averager math.Averager // Timeout is [timeoutCoefficient] * average response time @@ -148,6 +149,11 @@ func NewAdaptiveTimeoutManager( Name: "timeouts", Help: "Number of timed out requests", }), + numPendingTimeouts: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: metricsNamespace, + Name: "pending_timeouts", + Help: "Number of pending timeouts", + }), minimumTimeout: config.MinimumTimeout, maximumTimeout: config.MaximumTimeout, currentTimeout: config.InitialTimeout, @@ -158,9 +164,12 @@ func NewAdaptiveTimeoutManager( tm.averager = math.NewAverager(float64(config.InitialTimeout), config.TimeoutHalflife, tm.clock.Time()) errs := &wrappers.Errs{} - errs.Add(metricsRegister.Register(tm.networkTimeoutMetric)) - errs.Add(metricsRegister.Register(tm.avgLatency)) - errs.Add(metricsRegister.Register(tm.numTimeouts)) + errs.Add( + metricsRegister.Register(tm.networkTimeoutMetric), + metricsRegister.Register(tm.avgLatency), + metricsRegister.Register(tm.numTimeouts), + metricsRegister.Register(tm.numPendingTimeouts), + ) return tm, errs.Err } @@ -195,6 +204,7 @@ func (tm *adaptiveTimeoutManager) put(id ids.ID, op message.Op, handler func()) op: op, } tm.timeoutMap[id] = timeout + tm.numPendingTimeouts.Set(float64(len(tm.timeoutMap))) heap.Push(&tm.timeoutQueue, timeout) tm.setNextTimeoutTime() @@ -226,6 +236,7 @@ func (tm *adaptiveTimeoutManager) remove(id ids.ID, now time.Time) { // Remove the timeout from the map delete(tm.timeoutMap, id) + tm.numPendingTimeouts.Set(float64(len(tm.timeoutMap))) // Remove the timeout from the queue heap.Remove(&tm.timeoutQueue, timeout.index) diff --git a/avalanchego/utils/timer/adaptive_timeout_manager_test.go b/avalanchego/utils/timer/adaptive_timeout_manager_test.go index 91ed343b..3c8d2a80 100644 --- a/avalanchego/utils/timer/adaptive_timeout_manager_test.go +++ b/avalanchego/utils/timer/adaptive_timeout_manager_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/timer/eta.go b/avalanchego/utils/timer/eta.go index 3b56bf41..d45949a6 100644 --- a/avalanchego/utils/timer/eta.go +++ b/avalanchego/utils/timer/eta.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/timer/meter.go b/avalanchego/utils/timer/meter.go index 418e3233..4462fe58 100644 --- a/avalanchego/utils/timer/meter.go +++ b/avalanchego/utils/timer/meter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/timer/mockable/clock.go b/avalanchego/utils/timer/mockable/clock.go index cd9cb7e1..4c7cd138 100644 --- a/avalanchego/utils/timer/mockable/clock.go +++ b/avalanchego/utils/timer/mockable/clock.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package mockable diff --git a/avalanchego/utils/timer/mockable/clock_test.go b/avalanchego/utils/timer/mockable/clock_test.go index d80fcb21..a4ff8f75 100644 --- a/avalanchego/utils/timer/mockable/clock_test.go +++ b/avalanchego/utils/timer/mockable/clock_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package mockable diff --git a/avalanchego/utils/timer/repeater.go b/avalanchego/utils/timer/repeater.go deleted file mode 100644 index d8844e71..00000000 --- a/avalanchego/utils/timer/repeater.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package timer - -import ( - "sync" - "time" -) - -type Repeater struct { - handler func() - timeout chan struct{} - - lock sync.Mutex - wg sync.WaitGroup - finished bool - frequency time.Duration -} - -func NewRepeater(handler func(), frequency time.Duration) *Repeater { - repeater := &Repeater{ - handler: handler, - timeout: make(chan struct{}, 1), - frequency: frequency, - } - repeater.wg.Add(1) - - return repeater -} - -func (r *Repeater) Stop() { - r.lock.Lock() - if !r.finished { - defer r.wg.Wait() - } - defer r.lock.Unlock() - - r.finished = true - r.reset() -} - -func (r *Repeater) Dispatch() { - r.lock.Lock() - defer r.lock.Unlock() - defer r.wg.Done() - - timer := time.NewTimer(r.frequency) - cleared := false - for !r.finished { - r.lock.Unlock() - - cleared = false - select { - case <-r.timeout: - case <-timer.C: - cleared = true - } - - if !timer.Stop() && !cleared { - <-timer.C - } - - if cleared { - r.handler() - } - - timer.Reset(r.frequency) - - r.lock.Lock() - } -} - -func (r *Repeater) reset() { - select { - case r.timeout <- struct{}{}: - default: - } -} diff --git a/avalanchego/utils/timer/repeater_test.go b/avalanchego/utils/timer/repeater_test.go deleted file mode 100644 index b3f74105..00000000 --- a/avalanchego/utils/timer/repeater_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package timer - -import ( - "sync" - "testing" - "time" -) - -func TestRepeater(t *testing.T) { - wg := sync.WaitGroup{} - wg.Add(2) - - val := new(int) - repeater := NewRepeater(func() { - if *val < 2 { - wg.Done() - *val++ - } - }, time.Millisecond) - go repeater.Dispatch() - - wg.Wait() - repeater.Stop() -} diff --git a/avalanchego/utils/timer/staged_timer.go b/avalanchego/utils/timer/staged_timer.go index c807f8f3..5a05e670 100644 --- a/avalanchego/utils/timer/staged_timer.go +++ b/avalanchego/utils/timer/staged_timer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer @@ -8,6 +8,9 @@ import "time" // NewStagedTimer returns a timer that will execute [f] // when a timeout occurs and execute an additional timeout after // the returned duration if [f] returns true and some duration. +// +// Deprecated: NewStagedTimer exists for historical compatibility +// and should not be used. func NewStagedTimer(f func() (time.Duration, bool)) *Timer { t := NewTimer(nil) t.handler = func() { diff --git a/avalanchego/utils/timer/staged_timer_test.go b/avalanchego/utils/timer/staged_timer_test.go index 9c13c380..98b0160f 100644 --- a/avalanchego/utils/timer/staged_timer_test.go +++ b/avalanchego/utils/timer/staged_timer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/timer/timed_meter.go b/avalanchego/utils/timer/timed_meter.go deleted file mode 100644 index 344ac085..00000000 --- a/avalanchego/utils/timer/timed_meter.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package timer - -import ( - "container/list" - "sync" - "time" - - "github.com/ava-labs/avalanchego/utils/timer/mockable" -) - -var _ Meter = &TimedMeter{} - -// TimedMeter is a meter that discards old events -type TimedMeter struct { - lock sync.Mutex - // Can be used to fake time in tests - Clock *mockable.Clock - // Amount of time to keep a tick - Duration time.Duration - // TODO: Currently this list has an entry for each tick... This isn't really - // sustainable at high tick numbers. We should be batching ticks with - // similar times into the same bucket. - tickList *list.List -} - -func (tm *TimedMeter) Tick() { - tm.lock.Lock() - defer tm.lock.Unlock() - - tm.tick() -} - -func (tm *TimedMeter) Ticks() int { - tm.lock.Lock() - defer tm.lock.Unlock() - - return tm.ticks() -} - -func (tm *TimedMeter) init() { - if tm.tickList == nil { - tm.tickList = list.New() - } - if tm.Clock == nil { - tm.Clock = &mockable.Clock{} - } -} - -func (tm *TimedMeter) tick() { - tm.init() - tm.tickList.PushBack(tm.Clock.Time()) -} - -func (tm *TimedMeter) ticks() int { - tm.init() - - timeBound := tm.Clock.Time().Add(-tm.Duration) - // removeExpiredHead returns false once there is nothing left to remove - for tm.removeExpiredHead(timeBound) { - } - return tm.tickList.Len() -} - -// Returns true if the head was removed, false otherwise -func (tm *TimedMeter) removeExpiredHead(t time.Time) bool { - if tm.tickList.Len() == 0 { - return false - } - - head := tm.tickList.Front() - headTime := head.Value.(time.Time) - - if headTime.Before(t) { - tm.tickList.Remove(head) - return true - } - return false -} diff --git a/avalanchego/utils/timer/timeout_manager.go b/avalanchego/utils/timer/timeout_manager.go index 2545fc8e..4f60932d 100644 --- a/avalanchego/utils/timer/timeout_manager.go +++ b/avalanchego/utils/timer/timeout_manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/timer/timeout_manager_test.go b/avalanchego/utils/timer/timeout_manager_test.go index 512513c8..5a11c917 100644 --- a/avalanchego/utils/timer/timeout_manager_test.go +++ b/avalanchego/utils/timer/timeout_manager_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/timer/timer.go b/avalanchego/utils/timer/timer.go index 45292ab4..546980cc 100644 --- a/avalanchego/utils/timer/timer.go +++ b/avalanchego/utils/timer/timer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/timer/timer_test.go b/avalanchego/utils/timer/timer_test.go index 73c1de79..d3e049e2 100644 --- a/avalanchego/utils/timer/timer_test.go +++ b/avalanchego/utils/timer/timer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package timer diff --git a/avalanchego/utils/ulimit/ulimit_bsd.go b/avalanchego/utils/ulimit/ulimit_bsd.go index 06a6d1b9..c30bfdfb 100644 --- a/avalanchego/utils/ulimit/ulimit_bsd.go +++ b/avalanchego/utils/ulimit/ulimit_bsd.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. //go:build freebsd diff --git a/avalanchego/utils/ulimit/ulimit_darwin.go b/avalanchego/utils/ulimit/ulimit_darwin.go index 39ab93cf..77f2594a 100644 --- a/avalanchego/utils/ulimit/ulimit_darwin.go +++ b/avalanchego/utils/ulimit/ulimit_darwin.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. //go:build darwin diff --git a/avalanchego/utils/ulimit/ulimit_test.go b/avalanchego/utils/ulimit/ulimit_test.go index dbbb4abd..0ffe3aa1 100644 --- a/avalanchego/utils/ulimit/ulimit_test.go +++ b/avalanchego/utils/ulimit/ulimit_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ulimit @@ -6,14 +6,13 @@ package ulimit import ( "testing" - "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/utils/logging" ) // Test_SetDefault performs sanity checks for the os default. func Test_SetDefault(t *testing.T) { - require := require.New(t) err := Set(DefaultFDLimit, logging.NoLog{}) - require.NoErrorf(err, "default fd-limit failed %v", err) + if err != nil { + t.Skipf("default fd-limit failed %v", err) + } } diff --git a/avalanchego/utils/ulimit/ulimit_unix.go b/avalanchego/utils/ulimit/ulimit_unix.go index 29f6dd64..12d85e11 100644 --- a/avalanchego/utils/ulimit/ulimit_unix.go +++ b/avalanchego/utils/ulimit/ulimit_unix.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. //go:build linux || netbsd || openbsd diff --git a/avalanchego/utils/ulimit/ulimit_windows.go b/avalanchego/utils/ulimit/ulimit_windows.go index 43b9e601..693682af 100644 --- a/avalanchego/utils/ulimit/ulimit_windows.go +++ b/avalanchego/utils/ulimit/ulimit_windows.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. //go:build windows diff --git a/avalanchego/utils/units/avax.go b/avalanchego/utils/units/avax.go index 227b07af..c9eecc7e 100644 --- a/avalanchego/utils/units/avax.go +++ b/avalanchego/utils/units/avax.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package units diff --git a/avalanchego/utils/units/bytes.go b/avalanchego/utils/units/bytes.go index fc7f2d16..3b2fa5e0 100644 --- a/avalanchego/utils/units/bytes.go +++ b/avalanchego/utils/units/bytes.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package units diff --git a/avalanchego/utils/window/window.go b/avalanchego/utils/window/window.go index ed30373d..dc9f03d5 100644 --- a/avalanchego/utils/window/window.go +++ b/avalanchego/utils/window/window.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package window @@ -7,11 +7,21 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/buffer" "github.com/ava-labs/avalanchego/utils/timer/mockable" ) +var _ Window[struct{}] = &window[struct{}]{} + // Window is an interface which represents a sliding window of elements. -type Window struct { +type Window[T any] interface { + Add(value T) + Oldest() (T, bool) + Length() int +} + +type window[T any] struct { // mocked clock for unit testing clock *mockable.Clock // time-to-live for elements in the window @@ -22,11 +32,7 @@ type Window struct { // mutex for synchronization lock sync.Mutex // elements in the window - window []node - // head/tail pointers to mark occupied parts of the window - head, tail int - // how many elements are currently in the window - size int + elements buffer.UnboundedQueue[node[T]] } // Config exposes parameters for Window @@ -37,83 +43,72 @@ type Config struct { } // New returns an instance of window -func New(config Config) *Window { - return &Window{ - clock: config.Clock, - ttl: config.TTL, - maxSize: config.MaxSize, - window: make([]node, config.MaxSize), +func New[T any](config Config) Window[T] { + return &window[T]{ + clock: config.Clock, + ttl: config.TTL, + maxSize: config.MaxSize, + elements: buffer.NewUnboundedSliceQueue[node[T]](config.MaxSize + 1), } } // Add adds an element to a window and also evicts any elements if they've been // present in the window beyond the configured time-to-live -func (w *Window) Add(value interface{}) { +func (w *window[T]) Add(value T) { w.lock.Lock() defer w.lock.Unlock() w.removeStaleNodes() - if w.size >= w.maxSize { - w.removeOldestNode() + if w.elements.Len() >= w.maxSize { + _, _ = w.elements.Dequeue() } // add the new block id - w.window[w.tail] = node{ + w.elements.Enqueue(node[T]{ value: value, entryTime: w.clock.Time(), - } - w.tail = (w.tail + 1) % len(w.window) - w.size++ + }) } // Oldest returns the oldest element in the window. -func (w *Window) Oldest() (interface{}, bool) { +func (w *window[T]) Oldest() (T, bool) { w.lock.Lock() defer w.lock.Unlock() w.removeStaleNodes() - if w.size == 0 { - return nil, false - } - // fetch the oldest element - return w.window[w.head].value, true + oldest, ok := w.elements.PeekHead() + if !ok { + return utils.Zero[T](), false + } + return oldest.value, true } // Length returns the number of elements in the window. -func (w *Window) Length() int { +func (w *window[T]) Length() int { w.lock.Lock() defer w.lock.Unlock() w.removeStaleNodes() - return w.size + return w.elements.Len() } // removeStaleNodes removes any nodes beyond the configured ttl of a window node. -func (w *Window) removeStaleNodes() { +func (w *window[T]) removeStaleNodes() { // If we're beyond the expiry threshold, removeStaleNodes this node from our // window. Nodes are guaranteed to be strictly increasing in entry time, // so we can break this loop once we find the first non-stale one. - for w.size > 0 { - if w.clock.Time().Sub(w.window[w.head].entryTime) <= w.ttl { - break + for { + oldest, ok := w.elements.PeekHead() + if !ok || w.clock.Time().Sub(oldest.entryTime) <= w.ttl { + return } - - w.removeOldestNode() + _, _ = w.elements.Dequeue() } } -// Removes the oldest element. -// Doesn't actually remove anything, just marks that location in memory -// as available to overwrite. -func (w *Window) removeOldestNode() { - w.window[w.head].value = nil // mark for garbage collection - w.head = (w.head + 1) % len(w.window) - w.size-- -} - // helper struct to represent elements in the window -type node struct { - value interface{} +type node[T any] struct { + value T entryTime time.Time } diff --git a/avalanchego/utils/window/window_test.go b/avalanchego/utils/window/window_test.go index afb494de..0c3acdbc 100644 --- a/avalanchego/utils/window/window_test.go +++ b/avalanchego/utils/window/window_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package window @@ -42,7 +42,7 @@ func TestAdd(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - window := New( + window := New[int]( Config{ Clock: &mockable.Clock{}, MaxSize: testMaxSize, @@ -57,7 +57,7 @@ func TestAdd(t *testing.T) { require.Equal(t, len(test.window)+1, window.Length()) oldest, ok := window.Oldest() - require.Equal(t, test.expectedOldest, oldest.(int)) + require.Equal(t, test.expectedOldest, oldest) require.True(t, ok) }) } @@ -67,7 +67,7 @@ func TestAdd(t *testing.T) { // and needs to be evicted on Add. func TestTTLAdd(t *testing.T) { clock := mockable.Clock{} - window := New( + window := New[int]( Config{ Clock: &clock, MaxSize: testMaxSize, @@ -85,7 +85,7 @@ func TestTTLAdd(t *testing.T) { require.Equal(t, 3, window.Length()) oldest, ok := window.Oldest() - require.Equal(t, 1, oldest.(int)) + require.Equal(t, 1, oldest) require.True(t, ok) // Now we're one second past the ttl of 10 seconds as defined in testTTL, // so all existing elements need to be evicted. @@ -97,7 +97,7 @@ func TestTTLAdd(t *testing.T) { require.Equal(t, 1, window.Length()) oldest, ok = window.Oldest() - require.Equal(t, 4, oldest.(int)) + require.Equal(t, 4, oldest) require.True(t, ok) // Now we're one second past the ttl of 10 seconds of when [4] was added, // so all existing elements should be evicted. @@ -108,14 +108,14 @@ func TestTTLAdd(t *testing.T) { require.Equal(t, 0, window.Length()) oldest, ok = window.Oldest() - require.Nil(t, oldest) + require.Equal(t, 0, oldest) require.False(t, ok) } // TestTTLReadOnly tests that stale elements are still evicted on Length func TestTTLLength(t *testing.T) { clock := mockable.Clock{} - window := New( + window := New[int]( Config{ Clock: &clock, MaxSize: testMaxSize, @@ -144,13 +144,15 @@ func TestTTLLength(t *testing.T) { // TestTTLReadOnly tests that stale elements are still evicted on calling Oldest func TestTTLOldest(t *testing.T) { clock := mockable.Clock{} - window := New( + windowIntf := New[int]( Config{ Clock: &clock, MaxSize: testMaxSize, TTL: testTTL, }, ) + window, ok := windowIntf.(*window[int]) + require.True(t, ok) epochStart := time.Unix(0, 0) clock.Set(epochStart) @@ -161,9 +163,9 @@ func TestTTLOldest(t *testing.T) { window.Add(3) oldest, ok := window.Oldest() - require.Equal(t, 1, oldest.(int)) + require.Equal(t, 1, oldest) require.True(t, ok) - require.Equal(t, 3, window.size) + require.Equal(t, 3, window.elements.Len()) // Now we're one second past the ttl of 10 seconds as defined in testTTL, // so all existing elements need to be evicted. @@ -171,14 +173,14 @@ func TestTTLOldest(t *testing.T) { // Now there shouldn't be any elements in the window oldest, ok = window.Oldest() - require.Nil(t, oldest) + require.Equal(t, 0, oldest) require.False(t, ok) - require.Equal(t, 0, window.size) + require.Equal(t, 0, window.elements.Len()) } // Tests that we bound the amount of elements in the window func TestMaxCapacity(t *testing.T) { - window := New( + window := New[int]( Config{ Clock: &mockable.Clock{}, MaxSize: 3, @@ -207,6 +209,6 @@ func TestMaxCapacity(t *testing.T) { require.Equal(t, 3, window.Length()) oldest, ok := window.Oldest() - require.Equal(t, 4, oldest.(int)) + require.Equal(t, 4, oldest) require.True(t, ok) } diff --git a/avalanchego/utils/wrappers/closers.go b/avalanchego/utils/wrappers/closers.go index dc7d44ab..834c64e2 100644 --- a/avalanchego/utils/wrappers/closers.go +++ b/avalanchego/utils/wrappers/closers.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package wrappers diff --git a/avalanchego/utils/wrappers/errors.go b/avalanchego/utils/wrappers/errors.go index ab0139b8..2484f55d 100644 --- a/avalanchego/utils/wrappers/errors.go +++ b/avalanchego/utils/wrappers/errors.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package wrappers diff --git a/avalanchego/utils/wrappers/packing.go b/avalanchego/utils/wrappers/packing.go index af132964..081b9231 100644 --- a/avalanchego/utils/wrappers/packing.go +++ b/avalanchego/utils/wrappers/packing.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package wrappers diff --git a/avalanchego/utils/wrappers/packing_test.go b/avalanchego/utils/wrappers/packing_test.go index 2e49011c..7b45dc0e 100644 --- a/avalanchego/utils/wrappers/packing_test.go +++ b/avalanchego/utils/wrappers/packing_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package wrappers diff --git a/avalanchego/utils/zero.go b/avalanchego/utils/zero.go new file mode 100644 index 00000000..98120681 --- /dev/null +++ b/avalanchego/utils/zero.go @@ -0,0 +1,9 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +// Returns a new instance of a T. +func Zero[T any]() T { + return *new(T) //nolint:gocritic +} diff --git a/avalanchego/version/application.go b/avalanchego/version/application.go index 3ce5d106..f1c20206 100644 --- a/avalanchego/version/application.go +++ b/avalanchego/version/application.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/application_test.go b/avalanchego/version/application_test.go index 894778a0..ac78b355 100644 --- a/avalanchego/version/application_test.go +++ b/avalanchego/version/application_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/compatibility.go b/avalanchego/version/compatibility.go index ed714eba..b1b86e74 100644 --- a/avalanchego/version/compatibility.go +++ b/avalanchego/version/compatibility.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/compatibility_test.go b/avalanchego/version/compatibility_test.go index ead91600..4e675876 100644 --- a/avalanchego/version/compatibility_test.go +++ b/avalanchego/version/compatibility_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/constants.go b/avalanchego/version/constants.go index e2776d64..b734ded0 100644 --- a/avalanchego/version/constants.go +++ b/avalanchego/version/constants.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version @@ -13,8 +13,8 @@ import ( var ( Current = &Semantic{ Major: 1, - Minor: 7, - Patch: 1807, + Minor: 9, + Patch: 0, } CurrentApp = &Application{ Major: Current.Major, @@ -23,19 +23,19 @@ var ( } MinimumCompatibleVersion = &Application{ Major: 1, - Minor: 7, + Minor: 9, Patch: 0, } PrevMinimumCompatibleVersion = &Application{ Major: 1, - Minor: 6, - Patch: 0, + Minor: 7, + Patch: 1806, } CurrentSgb = &Semantic{ Major: 0, - Minor: 6, - Patch: 6, + Minor: 7, + Patch: 0, } CurrentSgbApp = &Application{ Major: CurrentSgb.Major, @@ -45,12 +45,12 @@ var ( MinimumCompatibleSgbVersion = &Application{ Major: 0, Minor: 6, - Patch: 5, + Patch: 6, } PrevMinimumCompatibleSgbVersion = &Application{ Major: 0, Minor: 6, - Patch: 4, + Patch: 6, } CurrentDatabase = DatabaseVersion1_4_5 @@ -104,17 +104,29 @@ var ( } ApricotPhase5DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) + ApricotPhase6Times = map[uint32]time.Time{ + constants.MainnetID: time.Date(2022, time.September, 6, 20, 0, 0, 0, time.UTC), + constants.FlareID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.CostwoID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.StagingID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.LocalFlareID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.CostonID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.SongbirdID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), + } + ApricotPhase6DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) + // FIXME: update this before release - BlueberryTimes = map[uint32]time.Time{ - constants.MainnetID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + BanffTimes = map[uint32]time.Time{ + constants.MainnetID: time.Date(2022, time.October, 18, 16, 0, 0, 0, time.UTC), constants.FlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.CostwoID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.StagingID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.CostonID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.SongbirdID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.LocalID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), } - BlueberryDefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) + BanffDefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC) // FIXME: update this before release XChainMigrationTimes = map[uint32]time.Time{ @@ -157,11 +169,18 @@ func GetApricotPhase5Time(networkID uint32) time.Time { return ApricotPhase5DefaultTime } -func GetBlueberryTime(networkID uint32) time.Time { - if upgradeTime, exists := BlueberryTimes[networkID]; exists { +func GetApricotPhase6Time(networkID uint32) time.Time { + if upgradeTime, exists := ApricotPhase6Times[networkID]; exists { + return upgradeTime + } + return ApricotPhase6DefaultTime +} + +func GetBanffTime(networkID uint32) time.Time { + if upgradeTime, exists := BanffTimes[networkID]; exists { return upgradeTime } - return BlueberryDefaultTime + return BanffDefaultTime } func GetXChainMigrationTime(networkID uint32) time.Time { @@ -183,7 +202,7 @@ func GetCompatibility(networkID uint32) Compatibility { return NewCompatibility( CurrentApp, MinimumCompatibleVersion, - GetApricotPhase5Time(networkID), + GetBanffTime(networkID), PrevMinimumCompatibleVersion, ) } diff --git a/avalanchego/version/parser.go b/avalanchego/version/parser.go index 27c2eb96..f39c0ee5 100644 --- a/avalanchego/version/parser.go +++ b/avalanchego/version/parser.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/parser_test.go b/avalanchego/version/parser_test.go index 682cc053..22d0d3ab 100644 --- a/avalanchego/version/parser_test.go +++ b/avalanchego/version/parser_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/string.go b/avalanchego/version/string.go index 74d7141c..2aab52fe 100644 --- a/avalanchego/version/string.go +++ b/avalanchego/version/string.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/version.go b/avalanchego/version/version.go index abd46774..68d4b677 100644 --- a/avalanchego/version/version.go +++ b/avalanchego/version/version.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/version/version_test.go b/avalanchego/version/version_test.go index 436437fd..ee07eb6b 100644 --- a/avalanchego/version/version_test.go +++ b/avalanchego/version/version_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package version diff --git a/avalanchego/vms/avm/client.go b/avalanchego/vms/avm/client.go index 61d3aae1..c1eeaa80 100644 --- a/avalanchego/vms/avm/client.go +++ b/avalanchego/vms/avm/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/client_test.go b/avalanchego/vms/avm/client_test.go index 996600cd..878a1c0f 100644 --- a/avalanchego/vms/avm/client_test.go +++ b/avalanchego/vms/avm/client_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/factory.go b/avalanchego/vms/avm/factory.go index 271cf05a..a1ccf7b1 100644 --- a/avalanchego/vms/avm/factory.go +++ b/avalanchego/vms/avm/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm @@ -16,8 +16,12 @@ type Factory struct { TxFee uint64 CreateAssetTxFee uint64 - // Time of the Blueberry network upgrade - BlueberryTime time.Time + // Time of the Banff network upgrade + BanffTime time.Time +} + +func (f *Factory) IsBanffActivated(timestamp time.Time) bool { + return !timestamp.Before(f.BanffTime) } func (f *Factory) New(*snow.Context) (interface{}, error) { diff --git a/avalanchego/vms/avm/fx_test.go b/avalanchego/vms/avm/fx_test.go index 200a5978..1e78246d 100644 --- a/avalanchego/vms/avm/fx_test.go +++ b/avalanchego/vms/avm/fx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/fxs/fx.go b/avalanchego/vms/avm/fxs/fx.go index bb7cdd98..7db0ab8c 100644 --- a/avalanchego/vms/avm/fxs/fx.go +++ b/avalanchego/vms/avm/fxs/fx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package fxs diff --git a/avalanchego/vms/avm/genesis.go b/avalanchego/vms/avm/genesis.go index 2562fffe..b1c8f899 100644 --- a/avalanchego/vms/avm/genesis.go +++ b/avalanchego/vms/avm/genesis.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/health.go b/avalanchego/vms/avm/health.go index 87debeca..9c788b7b 100644 --- a/avalanchego/vms/avm/health.go +++ b/avalanchego/vms/avm/health.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/index_test.go b/avalanchego/vms/avm/index_test.go index 22fd9184..93adce16 100644 --- a/avalanchego/vms/avm/index_test.go +++ b/avalanchego/vms/avm/index_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/metrics.go b/avalanchego/vms/avm/metrics.go index 22f96cb5..c3291fa3 100644 --- a/avalanchego/vms/avm/metrics.go +++ b/avalanchego/vms/avm/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/pubsub_filterer.go b/avalanchego/vms/avm/pubsub_filterer.go index 58dd3aba..bbe64529 100644 --- a/avalanchego/vms/avm/pubsub_filterer.go +++ b/avalanchego/vms/avm/pubsub_filterer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/pubsub_filterer_test.go b/avalanchego/vms/avm/pubsub_filterer_test.go index cb36ab67..5ae243f4 100644 --- a/avalanchego/vms/avm/pubsub_filterer_test.go +++ b/avalanchego/vms/avm/pubsub_filterer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/service.go b/avalanchego/vms/avm/service.go index ceb341d5..b946b8c2 100644 --- a/avalanchego/vms/avm/service.go +++ b/avalanchego/vms/avm/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/service_test.go b/avalanchego/vms/avm/service_test.go index 8ccc7d84..98585542 100644 --- a/avalanchego/vms/avm/service_test.go +++ b/avalanchego/vms/avm/service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/state_test.go b/avalanchego/vms/avm/state_test.go index e751e12d..4e054e80 100644 --- a/avalanchego/vms/avm/state_test.go +++ b/avalanchego/vms/avm/state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/states/state.go b/avalanchego/vms/avm/states/state.go index 10505497..6c4ae746 100644 --- a/avalanchego/vms/avm/states/state.go +++ b/avalanchego/vms/avm/states/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package states diff --git a/avalanchego/vms/avm/states/tx_state.go b/avalanchego/vms/avm/states/tx_state.go index a2e6cc7a..ca0c48ff 100644 --- a/avalanchego/vms/avm/states/tx_state.go +++ b/avalanchego/vms/avm/states/tx_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package states diff --git a/avalanchego/vms/avm/states/tx_state_test.go b/avalanchego/vms/avm/states/tx_state_test.go index 4ab58c47..d6d07269 100644 --- a/avalanchego/vms/avm/states/tx_state_test.go +++ b/avalanchego/vms/avm/states/tx_state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package states diff --git a/avalanchego/vms/avm/static_client.go b/avalanchego/vms/avm/static_client.go index 5386dafd..cb82a7fa 100644 --- a/avalanchego/vms/avm/static_client.go +++ b/avalanchego/vms/avm/static_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/static_service.go b/avalanchego/vms/avm/static_service.go index b6041afd..040dff34 100644 --- a/avalanchego/vms/avm/static_service.go +++ b/avalanchego/vms/avm/static_service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/static_service_test.go b/avalanchego/vms/avm/static_service_test.go index 428f98f8..df8b285f 100644 --- a/avalanchego/vms/avm/static_service_test.go +++ b/avalanchego/vms/avm/static_service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/tx_execute.go b/avalanchego/vms/avm/tx_execute.go index a7a38230..1e84b147 100644 --- a/avalanchego/vms/avm/tx_execute.go +++ b/avalanchego/vms/avm/tx_execute.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/tx_init.go b/avalanchego/vms/avm/tx_init.go index 437b0fe1..b0d023fe 100644 --- a/avalanchego/vms/avm/tx_init.go +++ b/avalanchego/vms/avm/tx_init.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/tx_semantic_verify.go b/avalanchego/vms/avm/tx_semantic_verify.go index bb58feb9..e66039cf 100644 --- a/avalanchego/vms/avm/tx_semantic_verify.go +++ b/avalanchego/vms/avm/tx_semantic_verify.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm @@ -98,11 +98,11 @@ func (t *txSemanticVerify) ExportTx(tx *txs.ExportTx) error { } assetID := out.AssetID() - if now.Before(t.vm.BlueberryTime) { - // TODO: Remove this check once the Blueberry network upgrade is + if !t.vm.IsBanffActivated(now) { + // TODO: Remove this check once the Banff network upgrade is // complete. // - // Blueberry network upgrade allows exporting of all assets to the + // Banff network upgrade allows exporting of all assets to the // P-chain. if assetID != t.vm.ctx.AVAXAssetID && tx.DestinationChain == constants.PlatformChainID { return errWrongAssetID diff --git a/avalanchego/vms/avm/tx_semantic_verify_test.go b/avalanchego/vms/avm/tx_semantic_verify_test.go index 34802065..65e44182 100644 --- a/avalanchego/vms/avm/tx_semantic_verify_test.go +++ b/avalanchego/vms/avm/tx_semantic_verify_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm @@ -1479,7 +1479,7 @@ func TestExportTxSemanticVerifyInvalidTransfer(t *testing.T) { } } -func TestExportTxSemanticVerifyTransferCustomAssetBeforeBlueberry(t *testing.T) { +func TestExportTxSemanticVerifyTransferCustomAssetBeforeBanff(t *testing.T) { genesisBytes, _, vm, _ := GenesisVM(t) ctx := vm.ctx defer func() { @@ -1489,7 +1489,7 @@ func TestExportTxSemanticVerifyTransferCustomAssetBeforeBlueberry(t *testing.T) ctx.Lock.Unlock() }() - vm.clock.Set(testBlueberryTime.Add(-time.Second)) + vm.clock.Set(testBanffTime.Add(-time.Second)) genesisAvaxTx := GetAVAXTxFromGenesisTest(genesisBytes, t) avaxID := genesisAvaxTx.ID() @@ -1581,7 +1581,7 @@ func TestExportTxSemanticVerifyTransferCustomAssetBeforeBlueberry(t *testing.T) } } -func TestExportTxSemanticVerifyTransferCustomAssetAfterBlueberry(t *testing.T) { +func TestExportTxSemanticVerifyTransferCustomAssetAfterBanff(t *testing.T) { genesisBytes, _, vm, _ := GenesisVM(t) ctx := vm.ctx defer func() { @@ -1591,7 +1591,7 @@ func TestExportTxSemanticVerifyTransferCustomAssetAfterBlueberry(t *testing.T) { ctx.Lock.Unlock() }() - vm.clock.Set(testBlueberryTime.Add(time.Second)) + vm.clock.Set(testBanffTime.Add(time.Second)) genesisAvaxTx := GetAVAXTxFromGenesisTest(genesisBytes, t) avaxID := genesisAvaxTx.ID() diff --git a/avalanchego/vms/avm/txs/base_tx.go b/avalanchego/vms/avm/txs/base_tx.go index b3e8b2d1..450962a4 100644 --- a/avalanchego/vms/avm/txs/base_tx.go +++ b/avalanchego/vms/avm/txs/base_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/base_tx_test.go b/avalanchego/vms/avm/txs/base_tx_test.go index fc9e5858..00f2172a 100644 --- a/avalanchego/vms/avm/txs/base_tx_test.go +++ b/avalanchego/vms/avm/txs/base_tx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/codec.go b/avalanchego/vms/avm/txs/codec.go index afc1a6b3..63f09738 100644 --- a/avalanchego/vms/avm/txs/codec.go +++ b/avalanchego/vms/avm/txs/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/create_asset_tx.go b/avalanchego/vms/avm/txs/create_asset_tx.go index 54ac405d..ebeebb97 100644 --- a/avalanchego/vms/avm/txs/create_asset_tx.go +++ b/avalanchego/vms/avm/txs/create_asset_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/create_asset_tx_test.go b/avalanchego/vms/avm/txs/create_asset_tx_test.go index 9865894e..2f7c03cd 100644 --- a/avalanchego/vms/avm/txs/create_asset_tx_test.go +++ b/avalanchego/vms/avm/txs/create_asset_tx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/export_tx.go b/avalanchego/vms/avm/txs/export_tx.go index 5d43ce46..5d9014fe 100644 --- a/avalanchego/vms/avm/txs/export_tx.go +++ b/avalanchego/vms/avm/txs/export_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/export_tx_test.go b/avalanchego/vms/avm/txs/export_tx_test.go index 8d8559aa..dd91b952 100644 --- a/avalanchego/vms/avm/txs/export_tx_test.go +++ b/avalanchego/vms/avm/txs/export_tx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/import_tx.go b/avalanchego/vms/avm/txs/import_tx.go index c659d128..efdc892b 100644 --- a/avalanchego/vms/avm/txs/import_tx.go +++ b/avalanchego/vms/avm/txs/import_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/import_tx_test.go b/avalanchego/vms/avm/txs/import_tx_test.go index 30d8a29b..19cea0a0 100644 --- a/avalanchego/vms/avm/txs/import_tx_test.go +++ b/avalanchego/vms/avm/txs/import_tx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/initial_state.go b/avalanchego/vms/avm/txs/initial_state.go index 5a4ccb55..1794fc5d 100644 --- a/avalanchego/vms/avm/txs/initial_state.go +++ b/avalanchego/vms/avm/txs/initial_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/initial_state_test.go b/avalanchego/vms/avm/txs/initial_state_test.go index 5f71ec48..142ae2c5 100644 --- a/avalanchego/vms/avm/txs/initial_state_test.go +++ b/avalanchego/vms/avm/txs/initial_state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/operation.go b/avalanchego/vms/avm/txs/operation.go index 1ddae496..4b025e7a 100644 --- a/avalanchego/vms/avm/txs/operation.go +++ b/avalanchego/vms/avm/txs/operation.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/operation_test.go b/avalanchego/vms/avm/txs/operation_test.go index bdd9373a..02d119fe 100644 --- a/avalanchego/vms/avm/txs/operation_test.go +++ b/avalanchego/vms/avm/txs/operation_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/operation_tx.go b/avalanchego/vms/avm/txs/operation_tx.go index 8213dad7..45239661 100644 --- a/avalanchego/vms/avm/txs/operation_tx.go +++ b/avalanchego/vms/avm/txs/operation_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/parser.go b/avalanchego/vms/avm/txs/parser.go index 9c013dc7..b070250b 100644 --- a/avalanchego/vms/avm/txs/parser.go +++ b/avalanchego/vms/avm/txs/parser.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/tx.go b/avalanchego/vms/avm/txs/tx.go index dc654dc9..2bb601fc 100644 --- a/avalanchego/vms/avm/txs/tx.go +++ b/avalanchego/vms/avm/txs/tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/tx_test.go b/avalanchego/vms/avm/txs/tx_test.go index 38ea9f75..462b3a4a 100644 --- a/avalanchego/vms/avm/txs/tx_test.go +++ b/avalanchego/vms/avm/txs/tx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/txs/visitor.go b/avalanchego/vms/avm/txs/visitor.go index b51f0371..6a11c745 100644 --- a/avalanchego/vms/avm/txs/visitor.go +++ b/avalanchego/vms/avm/txs/visitor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/avm/unique_tx.go b/avalanchego/vms/avm/unique_tx.go index 9d47df6a..a333edef 100644 --- a/avalanchego/vms/avm/unique_tx.go +++ b/avalanchego/vms/avm/unique_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/vm.go b/avalanchego/vms/avm/vm.go index a40bb197..87f7ff35 100644 --- a/avalanchego/vms/avm/vm.go +++ b/avalanchego/vms/avm/vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm @@ -568,7 +568,7 @@ func (vm *VM) verifyFxUsage(fxID int, assetID ids.ID) bool { // Check cache to see whether this asset supports this fx fxIDsIntf, assetInCache := vm.assetToFxCache.Get(assetID) if assetInCache { - return fxIDsIntf.(ids.BitSet).Contains(uint(fxID)) + return fxIDsIntf.(ids.BitSet64).Contains(uint(fxID)) } // Caches doesn't say whether this asset support this fx. // Get the tx that created the asset and check. @@ -584,7 +584,7 @@ func (vm *VM) verifyFxUsage(fxID int, assetID ids.ID) bool { // This transaction was not an asset creation tx return false } - fxIDs := ids.BitSet(0) + fxIDs := ids.BitSet64(0) for _, state := range createAssetTx.States { if state.FxIndex == uint32(fxID) { // Cache that this asset supports this fx diff --git a/avalanchego/vms/avm/vm_benchmark_test.go b/avalanchego/vms/avm/vm_benchmark_test.go index 98440e6e..4a24a920 100644 --- a/avalanchego/vms/avm/vm_benchmark_test.go +++ b/avalanchego/vms/avm/vm_benchmark_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/vm_test.go b/avalanchego/vms/avm/vm_test.go index d76e2460..2784d642 100644 --- a/avalanchego/vms/avm/vm_test.go +++ b/avalanchego/vms/avm/vm_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm @@ -43,11 +43,11 @@ import ( ) var ( - networkID uint32 = 10 - chainID = ids.ID{5, 4, 3, 2, 1} - testTxFee = uint64(1000) - testBlueberryTime = time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC) - startBalance = uint64(50000) + networkID uint32 = 10 + chainID = ids.ID{5, 4, 3, 2, 1} + testTxFee = uint64(1000) + testBanffTime = time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC) + startBalance = uint64(50000) keys []*crypto.PrivateKeySECP256K1R addrs []ids.ShortID // addrs[i] corresponds to keys[i] @@ -305,7 +305,7 @@ func GenesisVMWithArgs(tb testing.TB, additionalFxs []*common.Fx, args *BuildGen vm := &VM{Factory: Factory{ TxFee: testTxFee, CreateAssetTxFee: testTxFee, - BlueberryTime: testBlueberryTime, + BanffTime: testBanffTime, }} configBytes, err := stdjson.Marshal(Config{IndexTransactions: true}) if err != nil { diff --git a/avalanchego/vms/avm/wallet_client.go b/avalanchego/vms/avm/wallet_client.go index 57ff416b..5c4e732d 100644 --- a/avalanchego/vms/avm/wallet_client.go +++ b/avalanchego/vms/avm/wallet_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/wallet_service.go b/avalanchego/vms/avm/wallet_service.go index 402d72cf..9b6c2dc3 100644 --- a/avalanchego/vms/avm/wallet_service.go +++ b/avalanchego/vms/avm/wallet_service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/avm/wallet_service_test.go b/avalanchego/vms/avm/wallet_service_test.go index aec1fcf3..a400f7c9 100644 --- a/avalanchego/vms/avm/wallet_service_test.go +++ b/avalanchego/vms/avm/wallet_service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avm diff --git a/avalanchego/vms/components/avax/addresses.go b/avalanchego/vms/components/avax/addresses.go index 8d62ba04..db1f97b0 100644 --- a/avalanchego/vms/components/avax/addresses.go +++ b/avalanchego/vms/components/avax/addresses.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/asset.go b/avalanchego/vms/components/avax/asset.go index 828249b0..975c1e3c 100644 --- a/avalanchego/vms/components/avax/asset.go +++ b/avalanchego/vms/components/avax/asset.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/asset_test.go b/avalanchego/vms/components/avax/asset_test.go index 0ffad30e..37ab7a98 100644 --- a/avalanchego/vms/components/avax/asset_test.go +++ b/avalanchego/vms/components/avax/asset_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/atomic_utxos.go b/avalanchego/vms/components/avax/atomic_utxos.go index c560998b..b1293606 100644 --- a/avalanchego/vms/components/avax/atomic_utxos.go +++ b/avalanchego/vms/components/avax/atomic_utxos.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/base_tx.go b/avalanchego/vms/components/avax/base_tx.go index 472d7938..491d2afc 100644 --- a/avalanchego/vms/components/avax/base_tx.go +++ b/avalanchego/vms/components/avax/base_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/flow_checker.go b/avalanchego/vms/components/avax/flow_checker.go index 7a96744c..448fead0 100644 --- a/avalanchego/vms/components/avax/flow_checker.go +++ b/avalanchego/vms/components/avax/flow_checker.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/metadata.go b/avalanchego/vms/components/avax/metadata.go index 8929bf2d..6dcb9db3 100644 --- a/avalanchego/vms/components/avax/metadata.go +++ b/avalanchego/vms/components/avax/metadata.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/metadata_test.go b/avalanchego/vms/components/avax/metadata_test.go index 15cf5cd8..2e9c43fd 100644 --- a/avalanchego/vms/components/avax/metadata_test.go +++ b/avalanchego/vms/components/avax/metadata_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/mock_transferable_out.go b/avalanchego/vms/components/avax/mock_transferable_out.go new file mode 100644 index 00000000..a604af14 --- /dev/null +++ b/avalanchego/vms/components/avax/mock_transferable_out.go @@ -0,0 +1,89 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/vms/components/avax (interfaces: TransferableOut) + +// Package avax is a generated GoMock package. +package avax + +import ( + reflect "reflect" + + snow "github.com/ava-labs/avalanchego/snow" + gomock "github.com/golang/mock/gomock" +) + +// MockTransferableOut is a mock of TransferableOut interface. +type MockTransferableOut struct { + ctrl *gomock.Controller + recorder *MockTransferableOutMockRecorder +} + +// MockTransferableOutMockRecorder is the mock recorder for MockTransferableOut. +type MockTransferableOutMockRecorder struct { + mock *MockTransferableOut +} + +// NewMockTransferableOut creates a new mock instance. +func NewMockTransferableOut(ctrl *gomock.Controller) *MockTransferableOut { + mock := &MockTransferableOut{ctrl: ctrl} + mock.recorder = &MockTransferableOutMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTransferableOut) EXPECT() *MockTransferableOutMockRecorder { + return m.recorder +} + +// Amount mocks base method. +func (m *MockTransferableOut) Amount() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Amount") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// Amount indicates an expected call of Amount. +func (mr *MockTransferableOutMockRecorder) Amount() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Amount", reflect.TypeOf((*MockTransferableOut)(nil).Amount)) +} + +// InitCtx mocks base method. +func (m *MockTransferableOut) InitCtx(arg0 *snow.Context) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "InitCtx", arg0) +} + +// InitCtx indicates an expected call of InitCtx. +func (mr *MockTransferableOutMockRecorder) InitCtx(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitCtx", reflect.TypeOf((*MockTransferableOut)(nil).InitCtx), arg0) +} + +// Verify mocks base method. +func (m *MockTransferableOut) Verify() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Verify") + ret0, _ := ret[0].(error) + return ret0 +} + +// Verify indicates an expected call of Verify. +func (mr *MockTransferableOutMockRecorder) Verify() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockTransferableOut)(nil).Verify)) +} + +// VerifyState mocks base method. +func (m *MockTransferableOut) VerifyState() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifyState") + ret0, _ := ret[0].(error) + return ret0 +} + +// VerifyState indicates an expected call of VerifyState. +func (mr *MockTransferableOutMockRecorder) VerifyState() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyState", reflect.TypeOf((*MockTransferableOut)(nil).VerifyState)) +} diff --git a/avalanchego/vms/components/avax/singleton_state.go b/avalanchego/vms/components/avax/singleton_state.go index 467e1f7f..36113089 100644 --- a/avalanchego/vms/components/avax/singleton_state.go +++ b/avalanchego/vms/components/avax/singleton_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/singleton_state_test.go b/avalanchego/vms/components/avax/singleton_state_test.go index 5a82b19e..5b8e97dc 100644 --- a/avalanchego/vms/components/avax/singleton_state_test.go +++ b/avalanchego/vms/components/avax/singleton_state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/state.go b/avalanchego/vms/components/avax/state.go index 375ca8a3..181850a3 100644 --- a/avalanchego/vms/components/avax/state.go +++ b/avalanchego/vms/components/avax/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/status_state.go b/avalanchego/vms/components/avax/status_state.go index 86a57b02..acbc8474 100644 --- a/avalanchego/vms/components/avax/status_state.go +++ b/avalanchego/vms/components/avax/status_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/status_state_test.go b/avalanchego/vms/components/avax/status_state_test.go index 5cfb3914..955cb837 100644 --- a/avalanchego/vms/components/avax/status_state_test.go +++ b/avalanchego/vms/components/avax/status_state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/test_verifiable.go b/avalanchego/vms/components/avax/test_verifiable.go index 644f0859..499b25d6 100644 --- a/avalanchego/vms/components/avax/test_verifiable.go +++ b/avalanchego/vms/components/avax/test_verifiable.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/transferables.go b/avalanchego/vms/components/avax/transferables.go index 76ef481c..1ae44030 100644 --- a/avalanchego/vms/components/avax/transferables.go +++ b/avalanchego/vms/components/avax/transferables.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/transferables_test.go b/avalanchego/vms/components/avax/transferables_test.go index dba4c52f..23b79415 100644 --- a/avalanchego/vms/components/avax/transferables_test.go +++ b/avalanchego/vms/components/avax/transferables_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo.go b/avalanchego/vms/components/avax/utxo.go index 7a35c3dd..6e1c90d1 100644 --- a/avalanchego/vms/components/avax/utxo.go +++ b/avalanchego/vms/components/avax/utxo.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo_fetching.go b/avalanchego/vms/components/avax/utxo_fetching.go index cf8554e6..efc0afd1 100644 --- a/avalanchego/vms/components/avax/utxo_fetching.go +++ b/avalanchego/vms/components/avax/utxo_fetching.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo_fetching_test.go b/avalanchego/vms/components/avax/utxo_fetching_test.go index aed10ed6..978984cb 100644 --- a/avalanchego/vms/components/avax/utxo_fetching_test.go +++ b/avalanchego/vms/components/avax/utxo_fetching_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo_id.go b/avalanchego/vms/components/avax/utxo_id.go index 2afc03e2..b39177f7 100644 --- a/avalanchego/vms/components/avax/utxo_id.go +++ b/avalanchego/vms/components/avax/utxo_id.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo_id_test.go b/avalanchego/vms/components/avax/utxo_id_test.go index 036a3b4e..99a5e8b9 100644 --- a/avalanchego/vms/components/avax/utxo_id_test.go +++ b/avalanchego/vms/components/avax/utxo_id_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo_state.go b/avalanchego/vms/components/avax/utxo_state.go index 4b32b269..f8a7aed1 100644 --- a/avalanchego/vms/components/avax/utxo_state.go +++ b/avalanchego/vms/components/avax/utxo_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo_state_test.go b/avalanchego/vms/components/avax/utxo_state_test.go index a1360834..d9a6c6dc 100644 --- a/avalanchego/vms/components/avax/utxo_state_test.go +++ b/avalanchego/vms/components/avax/utxo_state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/avax/utxo_test.go b/avalanchego/vms/components/avax/utxo_test.go index 38ef4b06..600408bb 100644 --- a/avalanchego/vms/components/avax/utxo_test.go +++ b/avalanchego/vms/components/avax/utxo_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package avax diff --git a/avalanchego/vms/components/chain/block.go b/avalanchego/vms/components/chain/block.go index f934c9a3..8f946a93 100644 --- a/avalanchego/vms/components/chain/block.go +++ b/avalanchego/vms/components/chain/block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chain diff --git a/avalanchego/vms/components/chain/state.go b/avalanchego/vms/components/chain/state.go index 2698f84e..d2ca6f5d 100644 --- a/avalanchego/vms/components/chain/state.go +++ b/avalanchego/vms/components/chain/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chain @@ -104,7 +104,7 @@ func produceGetStatus(s *State, getBlockIDAtHeight func(uint64) (ids.ID, error)) internalBlk.SetStatus(choices.Processing) return choices.Processing, nil default: - return choices.Unknown, fmt.Errorf("failed to get accepted blkID at height %d", blkHeight) + return choices.Unknown, fmt.Errorf("%w: failed to get accepted blkID at height %d", err, blkHeight) } } } diff --git a/avalanchego/vms/components/chain/state_test.go b/avalanchego/vms/components/chain/state_test.go index a7b15883..c665d782 100644 --- a/avalanchego/vms/components/chain/state_test.go +++ b/avalanchego/vms/components/chain/state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package chain diff --git a/avalanchego/vms/components/index/index.go b/avalanchego/vms/components/index/index.go index 40fa6882..eceeddeb 100644 --- a/avalanchego/vms/components/index/index.go +++ b/avalanchego/vms/components/index/index.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package index diff --git a/avalanchego/vms/components/index/metrics.go b/avalanchego/vms/components/index/metrics.go index 94e1207c..4eb23264 100644 --- a/avalanchego/vms/components/index/metrics.go +++ b/avalanchego/vms/components/index/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package index diff --git a/avalanchego/vms/components/keystore/codec.go b/avalanchego/vms/components/keystore/codec.go index 733d69a1..fcebcaf8 100644 --- a/avalanchego/vms/components/keystore/codec.go +++ b/avalanchego/vms/components/keystore/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/vms/components/keystore/user.go b/avalanchego/vms/components/keystore/user.go index c3c85e00..9224cf89 100644 --- a/avalanchego/vms/components/keystore/user.go +++ b/avalanchego/vms/components/keystore/user.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/vms/components/keystore/user_test.go b/avalanchego/vms/components/keystore/user_test.go index 5330062e..11f64ea8 100644 --- a/avalanchego/vms/components/keystore/user_test.go +++ b/avalanchego/vms/components/keystore/user_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package keystore diff --git a/avalanchego/vms/components/state/builtin.go b/avalanchego/vms/components/state/builtin.go index 91f0c0f2..53bc80b5 100644 --- a/avalanchego/vms/components/state/builtin.go +++ b/avalanchego/vms/components/state/builtin.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/components/state/state.go b/avalanchego/vms/components/state/state.go index 8e4f89ae..cb927a8b 100644 --- a/avalanchego/vms/components/state/state.go +++ b/avalanchego/vms/components/state/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/components/state/state_test.go b/avalanchego/vms/components/state/state_test.go index bc8b511e..1230d1ae 100644 --- a/avalanchego/vms/components/state/state_test.go +++ b/avalanchego/vms/components/state/state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/components/state/types.go b/avalanchego/vms/components/state/types.go index 23f6b6c6..330b1cf8 100644 --- a/avalanchego/vms/components/state/types.go +++ b/avalanchego/vms/components/state/types.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/components/verify/mock_verifiable.go b/avalanchego/vms/components/verify/mock_verifiable.go new file mode 100644 index 00000000..9390c21a --- /dev/null +++ b/avalanchego/vms/components/verify/mock_verifiable.go @@ -0,0 +1,48 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/vms/components/verify (interfaces: Verifiable) + +// Package verify is a generated GoMock package. +package verify + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockVerifiable is a mock of Verifiable interface. +type MockVerifiable struct { + ctrl *gomock.Controller + recorder *MockVerifiableMockRecorder +} + +// MockVerifiableMockRecorder is the mock recorder for MockVerifiable. +type MockVerifiableMockRecorder struct { + mock *MockVerifiable +} + +// NewMockVerifiable creates a new mock instance. +func NewMockVerifiable(ctrl *gomock.Controller) *MockVerifiable { + mock := &MockVerifiable{ctrl: ctrl} + mock.recorder = &MockVerifiableMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVerifiable) EXPECT() *MockVerifiableMockRecorder { + return m.recorder +} + +// Verify mocks base method. +func (m *MockVerifiable) Verify() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Verify") + ret0, _ := ret[0].(error) + return ret0 +} + +// Verify indicates an expected call of Verify. +func (mr *MockVerifiableMockRecorder) Verify() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockVerifiable)(nil).Verify)) +} diff --git a/avalanchego/vms/components/verify/subnet.go b/avalanchego/vms/components/verify/subnet.go index 1b905a54..ac0c386e 100644 --- a/avalanchego/vms/components/verify/subnet.go +++ b/avalanchego/vms/components/verify/subnet.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package verify diff --git a/avalanchego/vms/components/verify/subnet_test.go b/avalanchego/vms/components/verify/subnet_test.go index 7e2d80a1..0bfaafe7 100644 --- a/avalanchego/vms/components/verify/subnet_test.go +++ b/avalanchego/vms/components/verify/subnet_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package verify diff --git a/avalanchego/vms/components/verify/verification.go b/avalanchego/vms/components/verify/verification.go index aed66aba..0ccef28c 100644 --- a/avalanchego/vms/components/verify/verification.go +++ b/avalanchego/vms/components/verify/verification.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package verify diff --git a/avalanchego/vms/components/verify/verification_test.go b/avalanchego/vms/components/verify/verification_test.go index 7f8b2234..c3fac1b4 100644 --- a/avalanchego/vms/components/verify/verification_test.go +++ b/avalanchego/vms/components/verify/verification_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package verify diff --git a/avalanchego/vms/manager.go b/avalanchego/vms/manager.go index 65d1dd6f..d11dd2cb 100644 --- a/avalanchego/vms/manager.go +++ b/avalanchego/vms/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package vms diff --git a/avalanchego/vms/metervm/batched_vm.go b/avalanchego/vms/metervm/batched_vm.go index 93a0a4d7..ddcc386c 100644 --- a/avalanchego/vms/metervm/batched_vm.go +++ b/avalanchego/vms/metervm/batched_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/block.go b/avalanchego/vms/metervm/block.go index a97dc3fd..ade13ca8 100644 --- a/avalanchego/vms/metervm/block.go +++ b/avalanchego/vms/metervm/block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/block_metrics.go b/avalanchego/vms/metervm/block_metrics.go index 53be62e9..514a5ea6 100644 --- a/avalanchego/vms/metervm/block_metrics.go +++ b/avalanchego/vms/metervm/block_metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/block_vm.go b/avalanchego/vms/metervm/block_vm.go index 1507f681..102e5437 100644 --- a/avalanchego/vms/metervm/block_vm.go +++ b/avalanchego/vms/metervm/block_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/height_indexed_vm.go b/avalanchego/vms/metervm/height_indexed_vm.go index 7f085f5d..2e96e023 100644 --- a/avalanchego/vms/metervm/height_indexed_vm.go +++ b/avalanchego/vms/metervm/height_indexed_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/metrics.go b/avalanchego/vms/metervm/metrics.go index 0af90696..b5db8d75 100644 --- a/avalanchego/vms/metervm/metrics.go +++ b/avalanchego/vms/metervm/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/state_syncable_vm.go b/avalanchego/vms/metervm/state_syncable_vm.go index faf444a3..20522d1f 100644 --- a/avalanchego/vms/metervm/state_syncable_vm.go +++ b/avalanchego/vms/metervm/state_syncable_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/vertex_metrics.go b/avalanchego/vms/metervm/vertex_metrics.go index 5883f2ff..07afc37b 100644 --- a/avalanchego/vms/metervm/vertex_metrics.go +++ b/avalanchego/vms/metervm/vertex_metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/metervm/vertex_vm.go b/avalanchego/vms/metervm/vertex_vm.go index 68edecd9..59995e1e 100644 --- a/avalanchego/vms/metervm/vertex_vm.go +++ b/avalanchego/vms/metervm/vertex_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metervm diff --git a/avalanchego/vms/nftfx/credential.go b/avalanchego/vms/nftfx/credential.go index 6cee958b..e98c7915 100644 --- a/avalanchego/vms/nftfx/credential.go +++ b/avalanchego/vms/nftfx/credential.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/credential_test.go b/avalanchego/vms/nftfx/credential_test.go index 3b5ee759..73e03413 100644 --- a/avalanchego/vms/nftfx/credential_test.go +++ b/avalanchego/vms/nftfx/credential_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/factory.go b/avalanchego/vms/nftfx/factory.go index 39afa583..ddcd585b 100644 --- a/avalanchego/vms/nftfx/factory.go +++ b/avalanchego/vms/nftfx/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/factory_test.go b/avalanchego/vms/nftfx/factory_test.go index 06ea1466..82dbafa7 100644 --- a/avalanchego/vms/nftfx/factory_test.go +++ b/avalanchego/vms/nftfx/factory_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/fx.go b/avalanchego/vms/nftfx/fx.go index bfd12e79..d61ee231 100644 --- a/avalanchego/vms/nftfx/fx.go +++ b/avalanchego/vms/nftfx/fx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/fx_test.go b/avalanchego/vms/nftfx/fx_test.go index e12dff89..22cae043 100644 --- a/avalanchego/vms/nftfx/fx_test.go +++ b/avalanchego/vms/nftfx/fx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/mint_operation.go b/avalanchego/vms/nftfx/mint_operation.go index cf1e5d9d..0616c475 100644 --- a/avalanchego/vms/nftfx/mint_operation.go +++ b/avalanchego/vms/nftfx/mint_operation.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/mint_operation_test.go b/avalanchego/vms/nftfx/mint_operation_test.go index 10c20bea..da9576dd 100644 --- a/avalanchego/vms/nftfx/mint_operation_test.go +++ b/avalanchego/vms/nftfx/mint_operation_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/mint_output.go b/avalanchego/vms/nftfx/mint_output.go index 2c834f92..93c849ac 100644 --- a/avalanchego/vms/nftfx/mint_output.go +++ b/avalanchego/vms/nftfx/mint_output.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/mint_output_test.go b/avalanchego/vms/nftfx/mint_output_test.go index dead6013..aae77829 100644 --- a/avalanchego/vms/nftfx/mint_output_test.go +++ b/avalanchego/vms/nftfx/mint_output_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/transfer_operation.go b/avalanchego/vms/nftfx/transfer_operation.go index 63669091..f4b8e41b 100644 --- a/avalanchego/vms/nftfx/transfer_operation.go +++ b/avalanchego/vms/nftfx/transfer_operation.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/transfer_operation_test.go b/avalanchego/vms/nftfx/transfer_operation_test.go index 9cc03aca..d6abc186 100644 --- a/avalanchego/vms/nftfx/transfer_operation_test.go +++ b/avalanchego/vms/nftfx/transfer_operation_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/transfer_output.go b/avalanchego/vms/nftfx/transfer_output.go index b554b0d6..a4adb5ed 100644 --- a/avalanchego/vms/nftfx/transfer_output.go +++ b/avalanchego/vms/nftfx/transfer_output.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/nftfx/transfer_output_test.go b/avalanchego/vms/nftfx/transfer_output_test.go index acc90277..954ba195 100644 --- a/avalanchego/vms/nftfx/transfer_output_test.go +++ b/avalanchego/vms/nftfx/transfer_output_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package nftfx diff --git a/avalanchego/vms/platformvm/api/static_client.go b/avalanchego/vms/platformvm/api/static_client.go index f91b52d3..9148d9a5 100644 --- a/avalanchego/vms/platformvm/api/static_client.go +++ b/avalanchego/vms/platformvm/api/static_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package api diff --git a/avalanchego/vms/platformvm/api/static_service.go b/avalanchego/vms/platformvm/api/static_service.go index ccc3f0e7..1f9ba9ac 100644 --- a/avalanchego/vms/platformvm/api/static_service.go +++ b/avalanchego/vms/platformvm/api/static_service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package api @@ -47,7 +47,8 @@ type UTXO struct { Message string `json:"message"` } -// TODO: refactor APIStaker, APIValidators and merge them together for SubnetValidators + PrimaryValidators +// TODO: Refactor APIStaker, APIValidators and merge them together for +// PermissionedValidators + PermissionlessValidators. // APIStaker is the representation of a staker sent via APIs. // [TxID] is the txID of the transaction that added this staker. @@ -71,23 +72,30 @@ type Owner struct { Addresses []string `json:"addresses"` } -// PrimaryValidator is the repr. of a primary network validator sent over APIs. -type PrimaryValidator struct { +// PermissionlessValidator is the repr. of a permissionless validator sent over +// APIs. +type PermissionlessValidator struct { Staker - // The owner the staking reward, if applicable, will go to - RewardOwner *Owner `json:"rewardOwner,omitempty"` - PotentialReward *json.Uint64 `json:"potentialReward,omitempty"` - DelegationFee json.Float32 `json:"delegationFee"` - ExactDelegationFee *json.Uint32 `json:"exactDelegationFee,omitempty"` - Uptime *json.Float32 `json:"uptime,omitempty"` - Connected bool `json:"connected"` - Staked []UTXO `json:"staked,omitempty"` + // Deprecated: RewardOwner has been replaced by ValidationRewardOwner and + // DelegationRewardOwner. + RewardOwner *Owner `json:"rewardOwner,omitempty"` + // The owner of the rewards from the validation period, if applicable. + ValidationRewardOwner *Owner `json:"validationRewardOwner,omitempty"` + // The owner of the rewards from delegations during the validation period, + // if applicable. + DelegationRewardOwner *Owner `json:"delegationRewardOwner,omitempty"` + PotentialReward *json.Uint64 `json:"potentialReward,omitempty"` + DelegationFee json.Float32 `json:"delegationFee"` + ExactDelegationFee *json.Uint32 `json:"exactDelegationFee,omitempty"` + Uptime *json.Float32 `json:"uptime,omitempty"` + Connected bool `json:"connected"` + Staked []UTXO `json:"staked,omitempty"` // The delegators delegating to this validator Delegators []PrimaryDelegator `json:"delegators"` } -// APISubnetValidator is the repr. of a subnet validator sent over APIs. -type SubnetValidator struct { +// PermissionedValidator is the repr. of a permissioned validator sent over APIs. +type PermissionedValidator struct { Staker // The owner the staking reward, if applicable, will go to Connected bool `json:"connected"` @@ -134,15 +142,15 @@ type Chain struct { // [Chains] are the chains that exist at genesis. // [Time] is the Platform Chain's time at network genesis. type BuildGenesisArgs struct { - AvaxAssetID ids.ID `json:"avaxAssetID"` - NetworkID json.Uint32 `json:"networkID"` - UTXOs []UTXO `json:"utxos"` - Validators []PrimaryValidator `json:"validators"` - Chains []Chain `json:"chains"` - Time json.Uint64 `json:"time"` - InitialSupply json.Uint64 `json:"initialSupply"` - Message string `json:"message"` - Encoding formatting.Encoding `json:"encoding"` + AvaxAssetID ids.ID `json:"avaxAssetID"` + NetworkID json.Uint32 `json:"networkID"` + UTXOs []UTXO `json:"utxos"` + Validators []PermissionlessValidator `json:"validators"` + Chains []Chain `json:"chains"` + Time json.Uint64 `json:"time"` + InitialSupply json.Uint64 `json:"initialSupply"` + Message string `json:"message"` + Encoding formatting.Encoding `json:"encoding"` } // BuildGenesisReply is the reply from BuildGenesis @@ -278,11 +286,11 @@ func (ss *StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, r End: uint64(vdr.EndTime), Wght: weight, }, - Stake: stake, - RewardsOwner: owner, - Shares: delegationFee, + StakeOuts: stake, + RewardsOwner: owner, + DelegationShares: delegationFee, }} - if err := tx.Sign(genesis.Codec, nil); err != nil { + if err := tx.Sign(txs.GenesisCodec, nil); err != nil { return err } @@ -308,7 +316,7 @@ func (ss *StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, r GenesisData: genesisBytes, SubnetAuth: &secp256k1fx.Input{}, }} - if err := tx.Sign(genesis.Codec, nil); err != nil { + if err := tx.Sign(txs.GenesisCodec, nil); err != nil { return err } @@ -328,7 +336,7 @@ func (ss *StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, r } // Marshal genesis to bytes - bytes, err := genesis.Codec.Marshal(txs.Version, g) + bytes, err := genesis.Codec.Marshal(genesis.Version, g) if err != nil { return fmt.Errorf("couldn't marshal genesis: %w", err) } diff --git a/avalanchego/vms/platformvm/api/static_service_test.go b/avalanchego/vms/platformvm/api/static_service_test.go index 9f154c73..d90735ce 100644 --- a/avalanchego/vms/platformvm/api/static_service_test.go +++ b/avalanchego/vms/platformvm/api/static_service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package api @@ -28,7 +28,7 @@ func TestBuildGenesisInvalidUTXOBalance(t *testing.T) { Amount: 0, } weight := json.Uint64(987654321) - validator := PrimaryValidator{ + validator := PermissionlessValidator{ Staker: Staker{ EndTime: 15, Weight: &weight, @@ -48,7 +48,7 @@ func TestBuildGenesisInvalidUTXOBalance(t *testing.T) { UTXOs: []UTXO{ utxo, }, - Validators: []PrimaryValidator{ + Validators: []PermissionlessValidator{ validator, }, Time: 5, @@ -74,7 +74,7 @@ func TestBuildGenesisInvalidAmount(t *testing.T) { Amount: 123456789, } weight := json.Uint64(0) - validator := PrimaryValidator{ + validator := PermissionlessValidator{ Staker: Staker{ StartTime: 0, EndTime: 15, @@ -94,7 +94,7 @@ func TestBuildGenesisInvalidAmount(t *testing.T) { UTXOs: []UTXO{ utxo, }, - Validators: []PrimaryValidator{ + Validators: []PermissionlessValidator{ validator, }, Time: 5, @@ -121,7 +121,7 @@ func TestBuildGenesisInvalidEndtime(t *testing.T) { } weight := json.Uint64(987654321) - validator := PrimaryValidator{ + validator := PermissionlessValidator{ Staker: Staker{ StartTime: 0, EndTime: 5, @@ -141,7 +141,7 @@ func TestBuildGenesisInvalidEndtime(t *testing.T) { UTXOs: []UTXO{ utxo, }, - Validators: []PrimaryValidator{ + Validators: []PermissionlessValidator{ validator, }, Time: 5, @@ -168,7 +168,7 @@ func TestBuildGenesisReturnsSortedValidators(t *testing.T) { } weight := json.Uint64(987654321) - validator1 := PrimaryValidator{ + validator1 := PermissionlessValidator{ Staker: Staker{ StartTime: 0, EndTime: 20, @@ -184,7 +184,7 @@ func TestBuildGenesisReturnsSortedValidators(t *testing.T) { }}, } - validator2 := PrimaryValidator{ + validator2 := PermissionlessValidator{ Staker: Staker{ StartTime: 3, EndTime: 15, @@ -200,7 +200,7 @@ func TestBuildGenesisReturnsSortedValidators(t *testing.T) { }}, } - validator3 := PrimaryValidator{ + validator3 := PermissionlessValidator{ Staker: Staker{ StartTime: 1, EndTime: 10, @@ -221,7 +221,7 @@ func TestBuildGenesisReturnsSortedValidators(t *testing.T) { UTXOs: []UTXO{ utxo, }, - Validators: []PrimaryValidator{ + Validators: []PermissionlessValidator{ validator1, validator2, validator3, diff --git a/avalanchego/vms/platformvm/blocks/abort_block.go b/avalanchego/vms/platformvm/blocks/abort_block.go new file mode 100644 index 00000000..9ab07071 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/abort_block.go @@ -0,0 +1,69 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package blocks + +import ( + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +var ( + _ BanffBlock = &BanffAbortBlock{} + _ Block = &ApricotAbortBlock{} +) + +type BanffAbortBlock struct { + Time uint64 `serialize:"true" json:"time"` + ApricotAbortBlock `serialize:"true"` +} + +func (b *BanffAbortBlock) Timestamp() time.Time { return time.Unix(int64(b.Time), 0) } +func (b *BanffAbortBlock) Visit(v Visitor) error { return v.BanffAbortBlock(b) } + +func NewBanffAbortBlock( + timestamp time.Time, + parentID ids.ID, + height uint64, +) (*BanffAbortBlock, error) { + blk := &BanffAbortBlock{ + Time: uint64(timestamp.Unix()), + ApricotAbortBlock: ApricotAbortBlock{ + CommonBlock: CommonBlock{ + PrntID: parentID, + Hght: height, + }, + }, + } + return blk, initialize(blk) +} + +type ApricotAbortBlock struct { + CommonBlock `serialize:"true"` +} + +func (b *ApricotAbortBlock) initialize(bytes []byte) error { + b.CommonBlock.initialize(bytes) + return nil +} + +func (*ApricotAbortBlock) InitCtx(ctx *snow.Context) {} + +func (*ApricotAbortBlock) Txs() []*txs.Tx { return nil } +func (b *ApricotAbortBlock) Visit(v Visitor) error { return v.ApricotAbortBlock(b) } + +func NewApricotAbortBlock( + parentID ids.ID, + height uint64, +) (*ApricotAbortBlock, error) { + blk := &ApricotAbortBlock{ + CommonBlock: CommonBlock{ + PrntID: parentID, + Hght: height, + }, + } + return blk, initialize(blk) +} diff --git a/avalanchego/vms/platformvm/blocks/abort_block_test.go b/avalanchego/vms/platformvm/blocks/abort_block_test.go new file mode 100644 index 00000000..6ccec281 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/abort_block_test.go @@ -0,0 +1,52 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package blocks + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" +) + +func TestNewBanffAbortBlock(t *testing.T) { + require := require.New(t) + + timestamp := time.Now().Truncate(time.Second) + parentID := ids.GenerateTestID() + height := uint64(1337) + blk, err := NewBanffAbortBlock( + timestamp, + parentID, + height, + ) + require.NoError(err) + + // Make sure the block is initialized + require.NotNil(blk.Bytes()) + + require.Equal(timestamp, blk.Timestamp()) + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} + +func TestNewApricotAbortBlock(t *testing.T) { + require := require.New(t) + + parentID := ids.GenerateTestID() + height := uint64(1337) + blk, err := NewApricotAbortBlock( + parentID, + height, + ) + require.NoError(err) + + // Make sure the block is initialized + require.NotNil(blk.Bytes()) + + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} diff --git a/avalanchego/vms/platformvm/blocks/atomic_block.go b/avalanchego/vms/platformvm/blocks/atomic_block.go index 0c784d1c..5ac8389b 100644 --- a/avalanchego/vms/platformvm/blocks/atomic_block.go +++ b/avalanchego/vms/platformvm/blocks/atomic_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks @@ -7,38 +7,40 @@ import ( "fmt" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) -var _ Block = &AtomicBlock{} +var _ Block = &ApricotAtomicBlock{} -// AtomicBlock being accepted results in the atomic transaction contained in the -// block to be accepted and committed to the chain. -type AtomicBlock struct { +// ApricotAtomicBlock being accepted results in the atomic transaction contained +// in the block to be accepted and committed to the chain. +type ApricotAtomicBlock struct { CommonBlock `serialize:"true"` Tx *txs.Tx `serialize:"true" json:"tx"` } -func (ab *AtomicBlock) initialize(bytes []byte) error { - ab.CommonBlock.initialize(bytes) - if err := ab.Tx.Sign(txs.Codec, nil); err != nil { +func (b *ApricotAtomicBlock) initialize(bytes []byte) error { + b.CommonBlock.initialize(bytes) + if err := b.Tx.Sign(txs.Codec, nil); err != nil { return fmt.Errorf("failed to initialize tx: %w", err) } return nil } -func (ab *AtomicBlock) Txs() []*txs.Tx { return []*txs.Tx{ab.Tx} } - -func (ab *AtomicBlock) Visit(v Visitor) error { - return v.AtomicBlock(ab) +func (b *ApricotAtomicBlock) InitCtx(ctx *snow.Context) { + b.Tx.Unsigned.InitCtx(ctx) } -func NewAtomicBlock( +func (b *ApricotAtomicBlock) Txs() []*txs.Tx { return []*txs.Tx{b.Tx} } +func (b *ApricotAtomicBlock) Visit(v Visitor) error { return v.ApricotAtomicBlock(b) } + +func NewApricotAtomicBlock( parentID ids.ID, height uint64, tx *txs.Tx, -) (*AtomicBlock, error) { - blk := &AtomicBlock{ +) (*ApricotAtomicBlock, error) { + blk := &ApricotAtomicBlock{ CommonBlock: CommonBlock{ PrntID: parentID, Hght: height, diff --git a/avalanchego/vms/platformvm/blocks/atomic_block_test.go b/avalanchego/vms/platformvm/blocks/atomic_block_test.go new file mode 100644 index 00000000..b2b510be --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/atomic_block_test.go @@ -0,0 +1,50 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package blocks + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +func TestNewApricotAtomicBlock(t *testing.T) { + require := require.New(t) + + parentID := ids.GenerateTestID() + height := uint64(1337) + tx := &txs.Tx{ + Unsigned: &txs.ImportTx{ + BaseTx: txs.BaseTx{ + BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{}, + Outs: []*avax.TransferableOutput{}, + }, + }, + ImportedInputs: []*avax.TransferableInput{}, + }, + Creds: []verify.Verifiable{}, + } + require.NoError(tx.Sign(txs.Codec, nil)) + + blk, err := NewApricotAtomicBlock( + parentID, + height, + tx, + ) + require.NoError(err) + + // Make sure the block and tx are initialized + require.NotNil(blk.Bytes()) + require.NotNil(blk.Tx.Bytes()) + require.NotEqual(ids.Empty, blk.Tx.ID()) + require.Equal(tx.Bytes(), blk.Tx.Bytes()) + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} diff --git a/avalanchego/vms/platformvm/blocks/block.go b/avalanchego/vms/platformvm/blocks/block.go index 01e4cfb4..8c8b5087 100644 --- a/avalanchego/vms/platformvm/blocks/block.go +++ b/avalanchego/vms/platformvm/blocks/block.go @@ -1,17 +1,20 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks import ( "fmt" + "time" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) // Block defines the common stateless interface for all blocks type Block interface { + snow.ContextInitializable ID() ids.ID Parent() ids.ID Bytes() []byte @@ -23,13 +26,20 @@ type Block interface { // Visit calls [visitor] with this block's concrete type Visit(visitor Visitor) error + // note: initialize does not assume that block transactions + // are initialized, and initializes them itself if they aren't. initialize(bytes []byte) error } +type BanffBlock interface { + Block + Timestamp() time.Time +} + func initialize(blk Block) error { // We serialize this block as a pointer so that it can be deserialized into // a Block - bytes, err := Codec.Marshal(txs.Version, &blk) + bytes, err := Codec.Marshal(Version, &blk) if err != nil { return fmt.Errorf("couldn't marshal block: %w", err) } diff --git a/avalanchego/vms/platformvm/blocks/builder/apricot_builder.go b/avalanchego/vms/platformvm/blocks/builder/apricot_builder.go new file mode 100644 index 00000000..8564fdf7 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/builder/apricot_builder.go @@ -0,0 +1,113 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package builder + +import ( + "fmt" + "time" + + "go.uber.org/zap" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" +) + +func buildApricotBlock( + builder *builder, + parentID ids.ID, + height uint64, + timestamp time.Time, + shouldAdvanceTime bool, + parentState state.Chain, +) (blocks.Block, error) { + // try including as many standard txs as possible. No need to advance chain time + if builder.Mempool.HasApricotDecisionTxs() { + return blocks.NewApricotStandardBlock( + parentID, + height, + builder.Mempool.PeekApricotDecisionTxs(targetBlockSize), + ) + } + + // try rewarding stakers whose staking period ends at current chain time. + stakerTxID, shouldReward, err := builder.getNextStakerToReward(parentState.GetTimestamp(), parentState) + if err != nil { + return nil, fmt.Errorf("could not find next staker to reward: %w", err) + } + if shouldReward { + rewardValidatorTx, err := builder.txBuilder.NewRewardValidatorTx(stakerTxID) + if err != nil { + return nil, fmt.Errorf("could not build tx to reward staker: %w", err) + } + + return blocks.NewApricotProposalBlock( + parentID, + height, + rewardValidatorTx, + ) + } + + // try advancing chain time + if shouldAdvanceTime { + advanceTimeTx, err := builder.txBuilder.NewAdvanceTimeTx(timestamp) + if err != nil { + return nil, fmt.Errorf("could not build tx to reward staker: %w", err) + } + + return blocks.NewApricotProposalBlock( + parentID, + height, + advanceTimeTx, + ) + } + + // Clean out transactions with an invalid timestamp. + builder.dropExpiredStakerTxs(timestamp) + + // Check the mempool + if !builder.Mempool.HasStakerTx() { + builder.txExecutorBackend.Ctx.Log.Debug("no pending txs to issue into a block") + return nil, errNoPendingBlocks + } + + tx, err := nextApricotProposalTx(builder, timestamp, parentState) + if err != nil { + builder.txExecutorBackend.Ctx.Log.Error( + "failed to get the next proposal tx", + zap.Error(err), + ) + return nil, err + } + + return blocks.NewApricotProposalBlock( + parentID, + height, + tx, + ) +} + +// Try to get/make a proposal tx to put into a block. +// Any returned error is unexpected. +func nextApricotProposalTx(builder *builder, timestamp time.Time, parentState state.Chain) (*txs.Tx, error) { + tx := builder.Mempool.PeekStakerTx() + startTime := tx.Unsigned.(txs.Staker).StartTime() + + // Check whether this staker starts within at most [MaxFutureStartTime]. + // If it does, issue the staking tx. + // If it doesn't, issue an advance time tx. + maxChainStartTime := parentState.GetTimestamp().Add(executor.MaxFutureStartTime) + if !startTime.After(maxChainStartTime) { + return tx, nil + } + + // The chain timestamp is too far in the past. Advance it. + advanceTimeTx, err := builder.txBuilder.NewAdvanceTimeTx(timestamp) + if err != nil { + return nil, fmt.Errorf("could not build tx to advance time: %w", err) + } + return advanceTimeTx, nil +} diff --git a/avalanchego/vms/platformvm/blocks/builder/apricot_builder_test.go b/avalanchego/vms/platformvm/blocks/builder/apricot_builder_test.go new file mode 100644 index 00000000..cb11ff9c --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/builder/apricot_builder_test.go @@ -0,0 +1,360 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package builder + +import ( + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + txbuilder "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" + txexecutor "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" +) + +func TestBuildApricotBlock(t *testing.T) { + var ( + parentID = ids.GenerateTestID() + height = uint64(1337) + output = &avax.TransferableOutput{ + Asset: avax.Asset{ID: ids.GenerateTestID()}, + Out: &secp256k1fx.TransferOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + }, + }, + } + now = time.Now() + parentTimestamp = now.Add(-2 * time.Second) + blockTxs = []*txs.Tx{{ + Unsigned: &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{{ + Asset: avax.Asset{ID: ids.GenerateTestID()}, + In: &secp256k1fx.TransferInput{ + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }}, + Outs: []*avax.TransferableOutput{output}, + }}, + Validator: validator.Validator{ + // Shouldn't be dropped + Start: uint64(now.Add(2 * txexecutor.SyncBound).Unix()), + }, + StakeOuts: []*avax.TransferableOutput{output}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + }, + }, + Creds: []verify.Verifiable{ + &secp256k1fx.Credential{ + Sigs: [][crypto.SECP256K1RSigLen]byte{{1, 3, 3, 7}}, + }, + }, + }} + stakerTxID = ids.GenerateTestID() + ) + + type test struct { + name string + builderF func(*gomock.Controller) *builder + timestamp time.Time + shouldAdvanceTime bool + parentStateF func(*gomock.Controller) state.Chain + expectedBlkF func(*require.Assertions) blocks.Block + expectedErr error + } + + tests := []test{ + { + name: "has decision txs", + builderF: func(ctrl *gomock.Controller) *builder { + mempool := mempool.NewMockMempool(ctrl) + mempool.EXPECT().HasApricotDecisionTxs().Return(true) + mempool.EXPECT().PeekApricotDecisionTxs(targetBlockSize).Return(blockTxs) + return &builder{ + Mempool: mempool, + } + }, + timestamp: time.Time{}, + shouldAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + return state.NewMockChain(ctrl) + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewApricotStandardBlock( + parentID, + height, + blockTxs, + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + { + name: "should reward", + builderF: func(ctrl *gomock.Controller) *builder { + // There are no decision txs + mempool := mempool.NewMockMempool(ctrl) + mempool.EXPECT().HasApricotDecisionTxs().Return(false) + + // The tx builder should be asked to build a reward tx + txBuilder := txbuilder.NewMockBuilder(ctrl) + txBuilder.EXPECT().NewRewardValidatorTx(stakerTxID).Return(blockTxs[0], nil) + + return &builder{ + Mempool: mempool, + txBuilder: txBuilder, + } + }, + timestamp: time.Time{}, + shouldAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + s.EXPECT().GetTimestamp().Return(parentTimestamp) + + // add current validator that ends at [parentTimestamp] + // i.e. it should be rewarded + currentStakerIter := state.NewMockStakerIterator(ctrl) + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + TxID: stakerTxID, + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + EndTime: parentTimestamp, + }) + currentStakerIter.EXPECT().Release() + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewApricotProposalBlock( + parentID, + height, + blockTxs[0], + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + { + name: "should advance time", + builderF: func(ctrl *gomock.Controller) *builder { + // There are no decision txs + mempool := mempool.NewMockMempool(ctrl) + mempool.EXPECT().HasApricotDecisionTxs().Return(false) + + // The tx builder should be asked to build an advance time tx + advanceTimeTx := &txs.Tx{Unsigned: &txs.AdvanceTimeTx{ + Time: uint64(now.Add(-1 * time.Second).Unix()), + }} + txBuilder := txbuilder.NewMockBuilder(ctrl) + txBuilder.EXPECT().NewAdvanceTimeTx(now.Add(-1*time.Second)).Return(advanceTimeTx, nil) + + clk := &mockable.Clock{} + clk.Set(now) + return &builder{ + Mempool: mempool, + txBuilder: txBuilder, + txExecutorBackend: &txexecutor.Backend{ + Clk: clk, + }, + } + }, + timestamp: now.Add(-1 * time.Second), + shouldAdvanceTime: true, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + s.EXPECT().GetTimestamp().Return(parentTimestamp) + + // add current validator that ends at [now] - 1 second. + // Handle calls in [getNextStakerToReward] + // and [GetNextStakerChangeTime] + // when determining whether to issue a reward tx. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(-1 * time.Second), + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewApricotProposalBlock( + parentID, + height, + &txs.Tx{Unsigned: &txs.AdvanceTimeTx{ // advances time + Time: uint64(now.Add(-1 * time.Second).Unix()), + }}, + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + { + name: "no proposal tx", + builderF: func(ctrl *gomock.Controller) *builder { + // There are no decision txs + mempool := mempool.NewMockMempool(ctrl) + mempool.EXPECT().HasApricotDecisionTxs().Return(false) + + // There is a staker tx. + mempool.EXPECT().HasStakerTx().Return(false).AnyTimes() + + clk := &mockable.Clock{} + clk.Set(now) + return &builder{ + Mempool: mempool, + txExecutorBackend: &txexecutor.Backend{ + Ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + Clk: clk, + }, + } + }, + timestamp: time.Time{}, + shouldAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + s.EXPECT().GetTimestamp().Return(parentTimestamp) + + // Next validator change time is in the future. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(time.Second), + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewApricotProposalBlock( + parentID, + height, + blockTxs[0], + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: errNoPendingBlocks, + }, + { + name: "has a proposal tx", + builderF: func(ctrl *gomock.Controller) *builder { + // There are no decision txs + mempool := mempool.NewMockMempool(ctrl) + mempool.EXPECT().HasApricotDecisionTxs().Return(false) + + // There is a proposal tx. + mempool.EXPECT().HasStakerTx().Return(true).AnyTimes() + mempool.EXPECT().PeekStakerTx().Return(blockTxs[0]).AnyTimes() + + clk := &mockable.Clock{} + clk.Set(now) + return &builder{ + Mempool: mempool, + txExecutorBackend: &txexecutor.Backend{ + Clk: clk, + }, + } + }, + timestamp: time.Time{}, + shouldAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + // Once in [buildBanffBlock], once in [GetNextStakerChangeTime], + s.EXPECT().GetTimestamp().Return(parentTimestamp).Times(2) + + // Handle calls in [getNextStakerToReward] + // and [GetNextStakerChangeTime]. + // Next validator change time is in the future. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(time.Second), + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewApricotProposalBlock( + parentID, + height, + blockTxs[0], + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gotBlk, err := buildApricotBlock( + tt.builderF(ctrl), + parentID, + height, + tt.timestamp, + tt.shouldAdvanceTime, + tt.parentStateF(ctrl), + ) + if tt.expectedErr != nil { + require.ErrorIs(err, tt.expectedErr) + return + } + require.NoError(err) + require.EqualValues(tt.expectedBlkF(require), gotBlk) + }) + } +} diff --git a/avalanchego/vms/platformvm/blocks/builder/banff_builder.go b/avalanchego/vms/platformvm/blocks/builder/banff_builder.go new file mode 100644 index 00000000..2ead5e32 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/builder/banff_builder.go @@ -0,0 +1,61 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package builder + +import ( + "fmt" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/state" +) + +// [timestamp] is min(max(now, parent timestamp), next staker change time) +func buildBanffBlock( + builder *builder, + parentID ids.ID, + height uint64, + timestamp time.Time, + forceAdvanceTime bool, + parentState state.Chain, +) (blocks.Block, error) { + // Try rewarding stakers whose staking period ends at the new chain time. + // This is done first to prioritize advancing the timestamp as quickly as + // possible. + stakerTxID, shouldReward, err := builder.getNextStakerToReward(timestamp, parentState) + if err != nil { + return nil, fmt.Errorf("could not find next staker to reward: %w", err) + } + if shouldReward { + rewardValidatorTx, err := builder.txBuilder.NewRewardValidatorTx(stakerTxID) + if err != nil { + return nil, fmt.Errorf("could not build tx to reward staker: %w", err) + } + + return blocks.NewBanffProposalBlock( + timestamp, + parentID, + height, + rewardValidatorTx, + ) + } + + // Clean out the mempool's transactions with invalid timestamps. + builder.dropExpiredStakerTxs(timestamp) + + // If there is no reason to build a block, don't. + if !builder.Mempool.HasTxs() && !forceAdvanceTime { + builder.txExecutorBackend.Ctx.Log.Debug("no pending txs to issue into a block") + return nil, errNoPendingBlocks + } + + // Issue a block with as many transactions as possible. + return blocks.NewBanffStandardBlock( + timestamp, + parentID, + height, + builder.Mempool.PeekTxs(targetBlockSize), + ) +} diff --git a/avalanchego/vms/platformvm/blocks/builder/banff_builder_test.go b/avalanchego/vms/platformvm/blocks/builder/banff_builder_test.go new file mode 100644 index 00000000..2f290a1c --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/builder/banff_builder_test.go @@ -0,0 +1,511 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package builder + +import ( + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + txbuilder "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" +) + +func TestBanffFork(t *testing.T) { + require := require.New(t) + + env := newEnvironment(t) + env.ctx.Lock.Lock() + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + + chainTime := env.state.GetTimestamp() + env.clk.Set(chainTime) + + apricotTimes := []time.Time{ + chainTime.Add(1 * executor.SyncBound), + chainTime.Add(2 * executor.SyncBound), + } + lastApricotTime := apricotTimes[len(apricotTimes)-1] + env.config.BanffTime = lastApricotTime.Add(time.Second) + + for i, nextValidatorStartTime := range apricotTimes { + // add a validator with the right start time + // so that we can then advance chain time to it + tx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, + uint64(nextValidatorStartTime.Unix()), + uint64(defaultValidateEndTime.Unix()), + ids.GenerateTestNodeID(), + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[i]}, + ids.ShortEmpty, + ) + require.NoError(err) + require.NoError(env.mempool.Add(tx)) + + proposalBlk, err := env.Builder.BuildBlock() + require.NoError(err) + require.NoError(proposalBlk.Verify()) + require.NoError(proposalBlk.Accept()) + require.NoError(env.state.Commit()) + + options, err := proposalBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk := options[0] + require.NoError(commitBlk.Verify()) + require.NoError(commitBlk.Accept()) + require.NoError(env.state.Commit()) + env.Builder.SetPreference(commitBlk.ID()) + + // advance chain time + env.clk.Set(nextValidatorStartTime) + advanceTimeBlk, err := env.Builder.BuildBlock() + require.NoError(err) + require.NoError(advanceTimeBlk.Verify()) + require.NoError(advanceTimeBlk.Accept()) + require.NoError(env.state.Commit()) + + options, err = advanceTimeBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk = options[0] + require.NoError(commitBlk.Verify()) + require.NoError(commitBlk.Accept()) + require.NoError(env.state.Commit()) + env.Builder.SetPreference(commitBlk.ID()) + } + + // set local clock at banff time, so to try and build a banff block + localTime := env.config.BanffTime + env.clk.Set(localTime) + + createChainTx, err := env.txBuilder.NewCreateChainTx( + testSubnet1.ID(), + nil, + constants.AVMID, + nil, + "chain name", + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + require.NoError(env.mempool.Add(createChainTx)) + + proposalBlk, err := env.Builder.BuildBlock() + require.NoError(err) + require.NoError(proposalBlk.Verify()) + require.NoError(proposalBlk.Accept()) + require.NoError(env.state.Commit()) + + // check Banff fork is activated + require.True(env.state.GetTimestamp().Equal(env.config.BanffTime)) +} + +func TestBuildBanffBlock(t *testing.T) { + var ( + parentID = ids.GenerateTestID() + height = uint64(1337) + output = &avax.TransferableOutput{ + Asset: avax.Asset{ID: ids.GenerateTestID()}, + Out: &secp256k1fx.TransferOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + }, + }, + } + now = time.Now() + parentTimestamp = now.Add(-2 * time.Second) + transactions = []*txs.Tx{{ + Unsigned: &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{{ + Asset: avax.Asset{ID: ids.GenerateTestID()}, + In: &secp256k1fx.TransferInput{ + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }}, + Outs: []*avax.TransferableOutput{output}, + }}, + Validator: validator.Validator{ + // Shouldn't be dropped + Start: uint64(now.Add(2 * executor.SyncBound).Unix()), + }, + StakeOuts: []*avax.TransferableOutput{output}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + }, + }, + Creds: []verify.Verifiable{ + &secp256k1fx.Credential{ + Sigs: [][crypto.SECP256K1RSigLen]byte{{1, 3, 3, 7}}, + }, + }, + }} + stakerTxID = ids.GenerateTestID() + ) + + type test struct { + name string + builderF func(*gomock.Controller) *builder + timestamp time.Time + forceAdvanceTime bool + parentStateF func(*gomock.Controller) state.Chain + expectedBlkF func(*require.Assertions) blocks.Block + expectedErr error + } + + tests := []test{ + { + name: "should reward", + builderF: func(ctrl *gomock.Controller) *builder { + mempool := mempool.NewMockMempool(ctrl) + + // The tx builder should be asked to build a reward tx + txBuilder := txbuilder.NewMockBuilder(ctrl) + txBuilder.EXPECT().NewRewardValidatorTx(stakerTxID).Return(transactions[0], nil) + + return &builder{ + Mempool: mempool, + txBuilder: txBuilder, + } + }, + timestamp: parentTimestamp, + forceAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + // add current validator that ends at [parentTimestamp] + // i.e. it should be rewarded + currentStakerIter := state.NewMockStakerIterator(ctrl) + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + TxID: stakerTxID, + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + EndTime: parentTimestamp, + }) + currentStakerIter.EXPECT().Release() + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewBanffProposalBlock( + parentTimestamp, + parentID, + height, + transactions[0], + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + { + name: "has decision txs", + builderF: func(ctrl *gomock.Controller) *builder { + mempool := mempool.NewMockMempool(ctrl) + + // There are txs. + mempool.EXPECT().HasStakerTx().Return(false) + mempool.EXPECT().HasTxs().Return(true) + mempool.EXPECT().PeekTxs(targetBlockSize).Return(transactions) + return &builder{ + Mempool: mempool, + } + }, + timestamp: parentTimestamp, + forceAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + // Handle calls in [getNextStakerToReward] + // and [GetNextStakerChangeTime]. + // Next validator change time is in the future. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(time.Second), + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewBanffStandardBlock( + parentTimestamp, + parentID, + height, + transactions, + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + { + name: "no stakers tx", + builderF: func(ctrl *gomock.Controller) *builder { + mempool := mempool.NewMockMempool(ctrl) + + // There are no txs. + mempool.EXPECT().HasStakerTx().Return(false) + mempool.EXPECT().HasTxs().Return(false) + + clk := &mockable.Clock{} + clk.Set(now) + return &builder{ + Mempool: mempool, + txExecutorBackend: &executor.Backend{ + Ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + Clk: clk, + }, + } + }, + timestamp: parentTimestamp, + forceAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + // Handle calls in [getNextStakerToReward] + // and [GetNextStakerChangeTime]. + // Next validator change time is in the future. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(time.Second), + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + return nil + }, + expectedErr: errNoPendingBlocks, + }, + { + name: "should advance time", + builderF: func(ctrl *gomock.Controller) *builder { + mempool := mempool.NewMockMempool(ctrl) + + // There are no txs. + mempool.EXPECT().HasStakerTx().Return(false) + mempool.EXPECT().HasTxs().Return(false) + mempool.EXPECT().PeekTxs(targetBlockSize).Return(nil) + + clk := &mockable.Clock{} + clk.Set(now) + return &builder{ + Mempool: mempool, + txExecutorBackend: &executor.Backend{ + Clk: clk, + }, + } + }, + timestamp: now.Add(-1 * time.Second), + forceAdvanceTime: true, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + // add current validator that ends at [now] - 1 second. + // That is, it ends in the past but after the current chain time. + // Handle calls in [getNextStakerToReward] + // and [GetNextStakerChangeTime] + // when determining whether to issue a reward tx. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(-1 * time.Second), + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewBanffStandardBlock( + now.Add(-1*time.Second), // note the advanced time + parentID, + height, + nil, // empty block to advance time + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + { + name: "has a staker tx no force", + builderF: func(ctrl *gomock.Controller) *builder { + mempool := mempool.NewMockMempool(ctrl) + + // There is a tx. + mempool.EXPECT().HasStakerTx().Return(false) + mempool.EXPECT().HasTxs().Return(true) + mempool.EXPECT().PeekTxs(targetBlockSize).Return([]*txs.Tx{transactions[0]}) + + clk := &mockable.Clock{} + clk.Set(now) + return &builder{ + Mempool: mempool, + txExecutorBackend: &executor.Backend{ + Clk: clk, + }, + } + }, + timestamp: parentTimestamp, + forceAdvanceTime: false, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + // Handle calls in [getNextStakerToReward] + // and [GetNextStakerChangeTime]. + // Next validator change time is in the future. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(time.Second), + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewBanffStandardBlock( + parentTimestamp, + parentID, + height, + []*txs.Tx{transactions[0]}, + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + { + name: "has a staker tx with force", + builderF: func(ctrl *gomock.Controller) *builder { + mempool := mempool.NewMockMempool(ctrl) + + // There are no decision txs + // There is a staker tx. + mempool.EXPECT().HasStakerTx().Return(false) + mempool.EXPECT().HasTxs().Return(true) + mempool.EXPECT().PeekTxs(targetBlockSize).Return([]*txs.Tx{transactions[0]}) + + clk := &mockable.Clock{} + clk.Set(now) + return &builder{ + Mempool: mempool, + txExecutorBackend: &executor.Backend{ + Clk: clk, + }, + } + }, + timestamp: parentTimestamp, + forceAdvanceTime: true, + parentStateF: func(ctrl *gomock.Controller) state.Chain { + s := state.NewMockChain(ctrl) + + // Handle calls in [getNextStakerToReward] + // and [GetNextStakerChangeTime]. + // Next validator change time is in the future. + currentStakerIter := state.NewMockStakerIterator(ctrl) + gomock.InOrder( + // expect calls from [getNextStakerToReward] + currentStakerIter.EXPECT().Next().Return(true), + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + NextTime: now.Add(time.Second), + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + }), + currentStakerIter.EXPECT().Release(), + ) + + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil).Times(1) + return s + }, + expectedBlkF: func(require *require.Assertions) blocks.Block { + expectedBlk, err := blocks.NewBanffStandardBlock( + parentTimestamp, + parentID, + height, + []*txs.Tx{transactions[0]}, + ) + require.NoError(err) + return expectedBlk + }, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + gotBlk, err := buildBanffBlock( + tt.builderF(ctrl), + parentID, + height, + tt.timestamp, + tt.forceAdvanceTime, + tt.parentStateF(ctrl), + ) + if tt.expectedErr != nil { + require.ErrorIs(err, tt.expectedErr) + return + } + require.NoError(err) + require.EqualValues(tt.expectedBlkF(require), gotBlk) + }) + } +} diff --git a/avalanchego/vms/platformvm/blocks/builder/builder.go b/avalanchego/vms/platformvm/blocks/builder/builder.go index 9c279888..d6657405 100644 --- a/avalanchego/vms/platformvm/blocks/builder/builder.go +++ b/avalanchego/vms/platformvm/blocks/builder/builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package builder @@ -55,7 +55,7 @@ type Builder interface { // next block BuildBlock() (snowman.Block, error) - // Shutdown cleanly shuts BlockBuilder down + // Shutdown cleanly shuts Builder down Shutdown() } @@ -147,7 +147,9 @@ func (b *builder) AddUnverifiedTx(tx *txs.Tx) error { return b.GossipTx(tx) } -// BuildBlock builds a block to be added to consensus +// BuildBlock builds a block to be added to consensus. +// This method removes the transactions from the returned +// blocks from the mempool. func (b *builder) BuildBlock() (snowman.Block, error) { b.Mempool.DisableAdding() defer func() { @@ -158,6 +160,21 @@ func (b *builder) BuildBlock() (snowman.Block, error) { ctx := b.txExecutorBackend.Ctx ctx.Log.Debug("starting to attempt to build a block") + statelessBlk, err := b.buildBlock() + if err != nil { + return nil, err + } + + // Remove selected txs from mempool now that we are returning the block to + // the consensus engine. + txs := statelessBlk.Txs() + b.Mempool.Remove(txs) + return b.blkManager.NewBlock(statelessBlk), nil +} + +// Returns the block we want to build and issue. +// Only modifies state to remove expired proposal txs. +func (b *builder) buildBlock() (blocks.Block, error) { // Get the block to build on top of and retrieve the new block's context. preferred, err := b.Preferred() if err != nil { @@ -165,97 +182,53 @@ func (b *builder) BuildBlock() (snowman.Block, error) { } preferredID := preferred.ID() nextHeight := preferred.Height() + 1 - preferredState, ok := b.blkManager.GetState(preferredID) if !ok { - return nil, fmt.Errorf("could not retrieve state for block %s", preferredID) + return nil, fmt.Errorf("%w: %s", state.ErrMissingParentState, preferredID) } - // Try building a standard block. - if b.Mempool.HasDecisionTxs() { - txs := b.Mempool.PopDecisionTxs(targetBlockSize) - statelessBlk, err := blocks.NewStandardBlock(preferredID, nextHeight, txs) - if err != nil { - return nil, err - } - return b.blkManager.NewBlock(statelessBlk), nil + timestamp := b.txExecutorBackend.Clk.Time() + if parentTime := preferred.Timestamp(); parentTime.After(timestamp) { + timestamp = parentTime } + // [timestamp] = max(now, parentTime) - // Try building a proposal block that rewards a staker. - stakerTxID, shouldReward, err := b.getNextStakerToReward(preferredState) + nextStakerChangeTime, err := txexecutor.GetNextStakerChangeTime(preferredState) if err != nil { - return nil, err - } - if shouldReward { - rewardValidatorTx, err := b.txBuilder.NewRewardValidatorTx(stakerTxID) - if err != nil { - return nil, err - } - statelessBlk, err := blocks.NewProposalBlock( - preferredID, - nextHeight, - rewardValidatorTx, - ) - if err != nil { - return nil, err - } - return b.blkManager.NewBlock(statelessBlk), nil + return nil, fmt.Errorf("could not calculate next staker change time: %w", err) } - // Try building a proposal block that advances the chain timestamp. - nextChainTime, shouldAdvanceTime, err := b.getNextChainTime(preferredState) - if err != nil { - return nil, err + // timeWasCapped means that [timestamp] was reduced to + // [nextStakerChangeTime]. It is used as a flag for [buildApricotBlock] to + // be willing to issue an advanceTimeTx. It is also used as a flag for + // [buildBanffBlock] to force the issuance of an empty block to advance + // the time forward; if there are no available transactions. + timeWasCapped := !timestamp.Before(nextStakerChangeTime) + if timeWasCapped { + timestamp = nextStakerChangeTime } - if shouldAdvanceTime { - advanceTimeTx, err := b.txBuilder.NewAdvanceTimeTx(nextChainTime) - if err != nil { - return nil, err - } - statelessBlk, err := blocks.NewProposalBlock( + // [timestamp] = min(max(now, parentTime), nextStakerChangeTime) + + // If the banff timestamp has come, build banff blocks. + if b.txExecutorBackend.Config.IsBanffActivated(timestamp) { + return buildBanffBlock( + b, preferredID, nextHeight, - advanceTimeTx, + timestamp, + timeWasCapped, + preferredState, ) - if err != nil { - return nil, err - } - return b.blkManager.NewBlock(statelessBlk), nil - } - - // Clean out the mempool's transactions with invalid timestamps. - if hasProposalTxs := b.dropTooEarlyMempoolProposalTxs(); !hasProposalTxs { - ctx.Log.Debug("no pending blocks to build") - return nil, errNoPendingBlocks - } - - // Get the proposal transaction that should be issued. - tx := b.Mempool.PopProposalTx() - startTime := tx.Unsigned.(txs.StakerTx).StartTime() - - // If the chain timestamp is too far in the past to issue this transaction - // but according to local time, it's ready to be issued, then attempt to - // advance the timestamp, so it can be issued. - maxChainStartTime := preferredState.GetTimestamp().Add(txexecutor.MaxFutureStartTime) - if startTime.After(maxChainStartTime) { - b.Mempool.AddProposalTx(tx) - - advanceTimeTx, err := b.txBuilder.NewAdvanceTimeTx(b.txExecutorBackend.Clk.Time()) - if err != nil { - return nil, err - } - statelessBlk, err := blocks.NewProposalBlock(preferredID, nextHeight, advanceTimeTx) - if err != nil { - return nil, err - } - return b.blkManager.NewBlock(statelessBlk), nil } - statelessBlk, err := blocks.NewProposalBlock(preferredID, nextHeight, tx) - if err != nil { - return nil, err - } - return b.blkManager.NewBlock(statelessBlk), nil + return buildApricotBlock( + b, + preferredID, + nextHeight, + timestamp, + timeWasCapped, + preferredState, + ) } func (b *builder) Shutdown() { @@ -274,14 +247,18 @@ func (b *builder) ResetBlockTimer() { } // getNextStakerToReward returns the next staker txID to remove from the staking -// set with a RewardValidatorTx rather than an AdvanceTimeTx. +// set with a RewardValidatorTx rather than an AdvanceTimeTx. [chainTimestamp] +// is the timestamp of the chain at the time this validator would be getting +// removed and is used to calculate [shouldReward]. // Returns: // - [txID] of the next staker to reward // - [shouldReward] if the txID exists and is ready to be rewarded // - [err] if something bad happened -func (b *builder) getNextStakerToReward(preferredState state.Chain) (ids.ID, bool, error) { - currentChainTimestamp := preferredState.GetTimestamp() - if !currentChainTimestamp.Before(mockable.MaxTime) { +func (b *builder) getNextStakerToReward( + chainTimestamp time.Time, + preferredState state.Chain, +) (ids.ID, bool, error) { + if !chainTimestamp.Before(mockable.MaxTime) { return ids.Empty, false, errEndOfTime } @@ -294,61 +271,44 @@ func (b *builder) getNextStakerToReward(preferredState state.Chain) (ids.ID, boo for currentStakerIterator.Next() { currentStaker := currentStakerIterator.Value() priority := currentStaker.Priority - // If the staker is a primary network staker (not a subnet validator), - // it's the next staker we will want to remove with a RewardValidatorTx - // rather than an AdvanceTimeTx. - if priority == state.PrimaryNetworkDelegatorCurrentPriority || - priority == state.PrimaryNetworkValidatorCurrentPriority { - return currentStaker.TxID, currentChainTimestamp.Equal(currentStaker.EndTime), nil + // If the staker is a permissionless staker (not a permissioned subnet + // validator), it's the next staker we will want to remove with a + // RewardValidatorTx rather than an AdvanceTimeTx. + if priority != txs.SubnetPermissionedValidatorCurrentPriority { + return currentStaker.TxID, chainTimestamp.Equal(currentStaker.EndTime), nil } } return ids.Empty, false, nil } -// getNextChainTime returns the timestamp for the next chain time and if the -// local time is >= time of the next staker set change. -func (b *builder) getNextChainTime(preferredState state.Chain) (time.Time, bool, error) { - nextStakerChangeTime, err := txexecutor.GetNextStakerChangeTime(preferredState) - if err != nil { - return time.Time{}, false, err - } - - now := b.txExecutorBackend.Clk.Time() - return nextStakerChangeTime, !now.Before(nextStakerChangeTime), nil -} - -// dropTooEarlyMempoolProposalTxs drops mempool's validators whose start time is -// too close in the future i.e. within local time plus Delta. -// dropTooEarlyMempoolProposalTxs makes sure that mempool's top proposal tx has -// a valid starting time but does not necessarily remove all txs since -// popped txs are not necessarily ordered by start time. -// Returns true/false if mempool is non-empty/empty following cleanup. -func (b *builder) dropTooEarlyMempoolProposalTxs() bool { - ctx := b.txExecutorBackend.Ctx - now := b.txExecutorBackend.Clk.Time() - syncTime := now.Add(txexecutor.SyncBound) - for b.Mempool.HasProposalTx() { - tx := b.Mempool.PopProposalTx() - startTime := tx.Unsigned.(txs.StakerTx).StartTime() - if !startTime.Before(syncTime) { - b.Mempool.AddProposalTx(tx) - return true +// dropExpiredStakerTxs drops add validator/delegator transactions in the +// mempool whose start time is not sufficiently far in the future +// (i.e. within local time plus [MaxFutureStartFrom]). +func (b *builder) dropExpiredStakerTxs(timestamp time.Time) { + minStartTime := timestamp.Add(txexecutor.SyncBound) + for b.Mempool.HasStakerTx() { + tx := b.Mempool.PeekStakerTx() + startTime := tx.Unsigned.(txs.Staker).StartTime() + if !startTime.Before(minStartTime) { + // The next proposal tx in the mempool starts sufficiently far in + // the future. + return } txID := tx.ID() errMsg := fmt.Sprintf( "synchrony bound (%s) is later than staker start time (%s)", - syncTime, + minStartTime, startTime, ) + b.Mempool.Remove([]*txs.Tx{tx}) b.Mempool.MarkDropped(txID, errMsg) // cache tx as dropped - ctx.Log.Debug("dropping tx", + b.txExecutorBackend.Ctx.Log.Debug("dropping tx", zap.String("reason", errMsg), zap.Stringer("txID", txID), ) } - return false } func (b *builder) setNextBuildBlockTime() { @@ -366,12 +326,13 @@ func (b *builder) setNextBuildBlockTime() { return } - // If there is a pending transaction trigger building of a block with that transaction - if b.Mempool.HasDecisionTxs() { + if _, err := b.buildBlock(); err == nil { + // We can build a block now b.notifyBlockReady() return } + // Wake up when it's time to add/remove the next validator/delegator preferredState, ok := b.blkManager.GetState(b.preferredBlockID) if !ok { // The preferred block should always be a decision block @@ -382,41 +343,6 @@ func (b *builder) setNextBuildBlockTime() { return } - _, shouldReward, err := b.getNextStakerToReward(preferredState) - if err != nil { - ctx.Log.Error("failed to fetch next staker to reward", - zap.Stringer("preferredID", b.preferredBlockID), - zap.Stringer("lastAcceptedID", b.blkManager.LastAccepted()), - zap.Error(err), - ) - return - } - if shouldReward { - b.notifyBlockReady() - return - } - - _, shouldAdvanceTime, err := b.getNextChainTime(preferredState) - if err != nil { - ctx.Log.Error("failed to fetch next chain time", - zap.Stringer("preferredID", b.preferredBlockID), - zap.Stringer("lastAcceptedID", b.blkManager.LastAccepted()), - zap.Error(err), - ) - return - } - if shouldAdvanceTime { - // time is at or after the time for the next validator to join/leave - b.notifyBlockReady() // Should issue a proposal to advance timestamp - return - } - - if hasProposalTxs := b.dropTooEarlyMempoolProposalTxs(); hasProposalTxs { - b.notifyBlockReady() // Should issue a ProposeAddValidator - return - } - - now := b.txExecutorBackend.Clk.Time() nextStakerChangeTime, err := txexecutor.GetNextStakerChangeTime(preferredState) if err != nil { ctx.Log.Error("couldn't get next staker change time", @@ -426,6 +352,8 @@ func (b *builder) setNextBuildBlockTime() { ) return } + + now := b.txExecutorBackend.Clk.Time() waitTime := nextStakerChangeTime.Sub(now) ctx.Log.Debug("setting next scheduled event", zap.Time("nextEventTime", nextStakerChangeTime), diff --git a/avalanchego/vms/platformvm/blocks/builder/builder_test.go b/avalanchego/vms/platformvm/blocks/builder/builder_test.go index e12c23c0..f4be39d3 100644 --- a/avalanchego/vms/platformvm/blocks/builder/builder_test.go +++ b/avalanchego/vms/platformvm/blocks/builder/builder_test.go @@ -1,18 +1,21 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package builder import ( - "math" "testing" + "time" + + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" + "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) @@ -21,7 +24,6 @@ import ( func TestBlockBuilderAddLocalTx(t *testing.T) { require := require.New(t) - validators.InitializeDefaultValidators(0) env := newEnvironment(t) env.ctx.Lock.Lock() defer func() { @@ -83,17 +85,7 @@ func TestPreviouslyDroppedTxsCanBeReAddedToMempool(t *testing.T) { // A previously dropped tx, popped then re-added to mempool, // is not dropped anymore - switch tx.Unsigned.(type) { - case txs.StakerTx: - env.mempool.PopProposalTx() - case *txs.CreateChainTx, - *txs.CreateSubnetTx, - *txs.ImportTx, - *txs.ExportTx: - env.mempool.PopDecisionTxs(math.MaxInt64) - default: - t.Fatal("unknown tx type") - } + env.mempool.Remove([]*txs.Tx{tx}) require.NoError(env.mempool.Add(tx)) require.True(env.mempool.Has(txID)) @@ -114,3 +106,187 @@ func TestNoErrorOnUnexpectedSetPreferenceDuringBootstrapping(t *testing.T) { env.Builder.SetPreference(ids.GenerateTestID()) // should not panic } + +func TestGetNextStakerToReward(t *testing.T) { + type test struct { + name string + timestamp time.Time + stateF func(*gomock.Controller) state.Chain + expectedTxID ids.ID + expectedShouldReward bool + expectedErr error + } + + var ( + now = time.Now() + txID = ids.GenerateTestID() + ) + tests := []test{ + { + name: "end of time", + timestamp: mockable.MaxTime, + stateF: func(ctrl *gomock.Controller) state.Chain { + return state.NewMockChain(ctrl) + }, + expectedErr: errEndOfTime, + }, + { + name: "no stakers", + timestamp: now, + stateF: func(ctrl *gomock.Controller) state.Chain { + currentStakerIter := state.NewMockStakerIterator(ctrl) + currentStakerIter.EXPECT().Next().Return(false) + currentStakerIter.EXPECT().Release() + + s := state.NewMockChain(ctrl) + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + + return s + }, + }, + { + name: "expired subnet validator/delegator", + timestamp: now, + stateF: func(ctrl *gomock.Controller) state.Chain { + currentStakerIter := state.NewMockStakerIterator(ctrl) + + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + Priority: txs.SubnetPermissionedValidatorCurrentPriority, + EndTime: now, + }) + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + TxID: txID, + Priority: txs.SubnetPermissionlessDelegatorCurrentPriority, + EndTime: now, + }) + currentStakerIter.EXPECT().Release() + + s := state.NewMockChain(ctrl) + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + + return s + }, + expectedTxID: txID, + expectedShouldReward: true, + }, + { + name: "expired primary network validator after subnet expired subnet validator", + timestamp: now, + stateF: func(ctrl *gomock.Controller) state.Chain { + currentStakerIter := state.NewMockStakerIterator(ctrl) + + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + Priority: txs.SubnetPermissionedValidatorCurrentPriority, + EndTime: now, + }) + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + TxID: txID, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + EndTime: now, + }) + currentStakerIter.EXPECT().Release() + + s := state.NewMockChain(ctrl) + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + + return s + }, + expectedTxID: txID, + expectedShouldReward: true, + }, + { + name: "expired primary network delegator after subnet expired subnet validator", + timestamp: now, + stateF: func(ctrl *gomock.Controller) state.Chain { + currentStakerIter := state.NewMockStakerIterator(ctrl) + + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + Priority: txs.SubnetPermissionedValidatorCurrentPriority, + EndTime: now, + }) + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + TxID: txID, + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + EndTime: now, + }) + currentStakerIter.EXPECT().Release() + + s := state.NewMockChain(ctrl) + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + + return s + }, + expectedTxID: txID, + expectedShouldReward: true, + }, + { + name: "non-expired primary network delegator", + timestamp: now, + stateF: func(ctrl *gomock.Controller) state.Chain { + currentStakerIter := state.NewMockStakerIterator(ctrl) + + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + TxID: txID, + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, + EndTime: now.Add(time.Second), + }) + currentStakerIter.EXPECT().Release() + + s := state.NewMockChain(ctrl) + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + + return s + }, + expectedTxID: txID, + expectedShouldReward: false, + }, + { + name: "non-expired primary network validator", + timestamp: now, + stateF: func(ctrl *gomock.Controller) state.Chain { + currentStakerIter := state.NewMockStakerIterator(ctrl) + + currentStakerIter.EXPECT().Next().Return(true) + currentStakerIter.EXPECT().Value().Return(&state.Staker{ + TxID: txID, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + EndTime: now.Add(time.Second), + }) + currentStakerIter.EXPECT().Release() + + s := state.NewMockChain(ctrl) + s.EXPECT().GetCurrentStakerIterator().Return(currentStakerIter, nil) + + return s + }, + expectedTxID: txID, + expectedShouldReward: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + state := tt.stateF(ctrl) + b := builder{} + txID, shouldReward, err := b.getNextStakerToReward(tt.timestamp, state) + if tt.expectedErr != nil { + require.Equal(tt.expectedErr, err) + return + } + require.NoError(err) + require.Equal(tt.expectedTxID, txID) + require.Equal(tt.expectedShouldReward, shouldReward) + }) + } +} diff --git a/avalanchego/vms/platformvm/blocks/builder/helpers_test.go b/avalanchego/vms/platformvm/blocks/builder/helpers_test.go index 2ef3faba..c1144bfa 100644 --- a/avalanchego/vms/platformvm/blocks/builder/helpers_test.go +++ b/avalanchego/vms/platformvm/blocks/builder/helpers_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package builder @@ -118,17 +118,13 @@ func (sn *snLookup) SubnetID(chainID ids.ID) (ids.ID, error) { } func newEnvironment(t *testing.T) *environment { - var ( - res = &environment{} - err error - ) - - res.isBootstrapped = &utils.AtomicBool{} + res := &environment{ + isBootstrapped: &utils.AtomicBool{}, + config: defaultConfig(), + clk: defaultClock(), + } res.isBootstrapped.SetValue(true) - res.config = defaultConfig() - res.clk = defaultClock() - baseDBManager := manager.NewMemDB(version.Semantic1_0_0) res.baseDB = versiondb.New(baseDBManager.Current().Database) res.ctx, res.msm = defaultCtx(res.baseDB) @@ -147,7 +143,7 @@ func newEnvironment(t *testing.T) *environment { res.txBuilder = txbuilder.New( res.ctx, - *res.config, + res.config, res.clk, res.fx, res.state, @@ -168,7 +164,7 @@ func newEnvironment(t *testing.T) *environment { } registerer := prometheus.NewRegistry() - window := window.New( + window := window.New[ids.ID]( window.Config{ Clock: res.clk, MaxSize: maxRecentlyAcceptedWindowSize, @@ -209,9 +205,7 @@ func newEnvironment(t *testing.T) *environment { return res } -func addSubnet( - env *environment, -) { +func addSubnet(env *environment) { // Create a subnet var err error testSubnet1, err = env.txBuilder.NewCreateSubnetTx( @@ -328,6 +322,7 @@ func defaultConfig() *config.Config { }, ApricotPhase3Time: defaultValidateEndTime, ApricotPhase5Time: defaultValidateEndTime, + BanffTime: mockable.MaxTime, } } @@ -380,14 +375,14 @@ func buildGenesisTest(ctx *snow.Context) []byte { } } - genesisValidators := make([]api.PrimaryValidator, len(preFundedKeys)) + genesisValidators := make([]api.PermissionlessValidator, len(preFundedKeys)) for i, key := range preFundedKeys { nodeID := ids.NodeID(key.PublicKey().Address()) addr, err := address.FormatBech32(hrp, nodeID.Bytes()) if err != nil { panic(err) } - genesisValidators[i] = api.PrimaryValidator{ + genesisValidators[i] = api.PermissionlessValidator{ Staker: api.Staker{ StartTime: json.Uint64(defaultValidateStartTime.Unix()), EndTime: json.Uint64(defaultValidateEndTime.Unix()), diff --git a/avalanchego/vms/platformvm/blocks/builder/network.go b/avalanchego/vms/platformvm/blocks/builder/network.go index 1a8731f9..cb023e81 100644 --- a/avalanchego/vms/platformvm/blocks/builder/network.go +++ b/avalanchego/vms/platformvm/blocks/builder/network.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // TODO: consider moving the network implementation to a separate package diff --git a/avalanchego/vms/platformvm/blocks/builder/network_test.go b/avalanchego/vms/platformvm/blocks/builder/network_test.go index c26880c5..00c6d6df 100644 --- a/avalanchego/vms/platformvm/blocks/builder/network_test.go +++ b/avalanchego/vms/platformvm/blocks/builder/network_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package builder @@ -148,7 +148,7 @@ func TestMempoolNewLocaTxIsGossiped(t *testing.T) { // show that transaction is not re-gossiped is recently added to mempool gossipedBytes = nil - env.Builder.RemoveDecisionTxs([]*txs.Tx{tx}) + env.Builder.Remove([]*txs.Tx{tx}) err = env.Builder.Add(tx) require.NoError(err, "could not reintroduce tx to mempool") diff --git a/avalanchego/vms/platformvm/blocks/builder/standard_block_test.go b/avalanchego/vms/platformvm/blocks/builder/standard_block_test.go index f91bf872..140d2192 100644 --- a/avalanchego/vms/platformvm/blocks/builder/standard_block_test.go +++ b/avalanchego/vms/platformvm/blocks/builder/standard_block_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package builder @@ -14,7 +14,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/vms/components/avax" - "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/secp256k1fx" @@ -53,7 +52,7 @@ func TestAtomicTxImports(t *testing.T) { }, }, } - utxoBytes, err := blocks.Codec.Marshal(txs.Version, utxo) + utxoBytes, err := txs.Codec.Marshal(txs.Version, utxo) require.NoError(err) inputID := utxo.InputID() @@ -78,8 +77,8 @@ func TestAtomicTxImports(t *testing.T) { env.state.SetTimestamp(env.config.ApricotPhase5Time.Add(100 * time.Second)) - env.Builder.AddDecisionTx(tx) - b, err := env.BuildBlock() + require.NoError(env.Builder.Add(tx)) + b, err := env.Builder.BuildBlock() require.NoError(err) // Test multiple verify calls work require.NoError(b.Verify()) diff --git a/avalanchego/vms/platformvm/blocks/codec.go b/avalanchego/vms/platformvm/blocks/codec.go index 17986687..cd019c72 100644 --- a/avalanchego/vms/platformvm/blocks/codec.go +++ b/avalanchego/vms/platformvm/blocks/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks @@ -12,6 +12,9 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) +// Version is the current default codec version +const Version = txs.Version + // GenesisCode allows blocks of larger than usual size to be parsed. // While this gives flexibility in accommodating large genesis blocks // it must not be used to parse new, unverified blocks which instead @@ -30,31 +33,43 @@ func init() { errs := wrappers.Errs{} for _, c := range []codec.Registry{c, gc} { errs.Add( - RegisterBlockTypes(c), + RegisterApricotBlockTypes(c), txs.RegisterUnsignedTxsTypes(c), + RegisterBanffBlockTypes(c), ) } errs.Add( - Codec.RegisterCodec(txs.Version, c), - GenesisCodec.RegisterCodec(txs.Version, gc), + Codec.RegisterCodec(Version, c), + GenesisCodec.RegisterCodec(Version, gc), ) if errs.Errored() { panic(errs.Err) } } -// RegisterBlockTypes allows registering relevant type of blocks package +// RegisterApricotBlockTypes allows registering relevant type of blocks package // in the right sequence. Following repackaging of platformvm package, a few // subpackage-level codecs were introduced, each handling serialization of // specific types. -func RegisterBlockTypes(targetCodec codec.Registry) error { +func RegisterApricotBlockTypes(targetCodec codec.Registry) error { + errs := wrappers.Errs{} + errs.Add( + targetCodec.RegisterType(&ApricotProposalBlock{}), + targetCodec.RegisterType(&ApricotAbortBlock{}), + targetCodec.RegisterType(&ApricotCommitBlock{}), + targetCodec.RegisterType(&ApricotStandardBlock{}), + targetCodec.RegisterType(&ApricotAtomicBlock{}), + ) + return errs.Err +} + +func RegisterBanffBlockTypes(targetCodec codec.Registry) error { errs := wrappers.Errs{} errs.Add( - targetCodec.RegisterType(&ProposalBlock{}), - targetCodec.RegisterType(&AbortBlock{}), - targetCodec.RegisterType(&CommitBlock{}), - targetCodec.RegisterType(&StandardBlock{}), - targetCodec.RegisterType(&AtomicBlock{}), + targetCodec.RegisterType(&BanffProposalBlock{}), + targetCodec.RegisterType(&BanffAbortBlock{}), + targetCodec.RegisterType(&BanffCommitBlock{}), + targetCodec.RegisterType(&BanffStandardBlock{}), ) return errs.Err } diff --git a/avalanchego/vms/platformvm/blocks/commit_block.go b/avalanchego/vms/platformvm/blocks/commit_block.go new file mode 100644 index 00000000..543af466 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/commit_block.go @@ -0,0 +1,69 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package blocks + +import ( + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +var ( + _ BanffBlock = &BanffCommitBlock{} + _ Block = &ApricotCommitBlock{} +) + +type BanffCommitBlock struct { + Time uint64 `serialize:"true" json:"time"` + ApricotCommitBlock `serialize:"true"` +} + +func (b *BanffCommitBlock) Timestamp() time.Time { return time.Unix(int64(b.Time), 0) } +func (b *BanffCommitBlock) Visit(v Visitor) error { return v.BanffCommitBlock(b) } + +func NewBanffCommitBlock( + timestamp time.Time, + parentID ids.ID, + height uint64, +) (*BanffCommitBlock, error) { + blk := &BanffCommitBlock{ + Time: uint64(timestamp.Unix()), + ApricotCommitBlock: ApricotCommitBlock{ + CommonBlock: CommonBlock{ + PrntID: parentID, + Hght: height, + }, + }, + } + return blk, initialize(blk) +} + +type ApricotCommitBlock struct { + CommonBlock `serialize:"true"` +} + +func (b *ApricotCommitBlock) initialize(bytes []byte) error { + b.CommonBlock.initialize(bytes) + return nil +} + +func (*ApricotCommitBlock) InitCtx(ctx *snow.Context) {} + +func (*ApricotCommitBlock) Txs() []*txs.Tx { return nil } +func (b *ApricotCommitBlock) Visit(v Visitor) error { return v.ApricotCommitBlock(b) } + +func NewApricotCommitBlock( + parentID ids.ID, + height uint64, +) (*ApricotCommitBlock, error) { + blk := &ApricotCommitBlock{ + CommonBlock: CommonBlock{ + PrntID: parentID, + Hght: height, + }, + } + return blk, initialize(blk) +} diff --git a/avalanchego/vms/platformvm/blocks/commit_block_test.go b/avalanchego/vms/platformvm/blocks/commit_block_test.go new file mode 100644 index 00000000..6a3e428b --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/commit_block_test.go @@ -0,0 +1,52 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package blocks + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" +) + +func TestNewBanffCommitBlock(t *testing.T) { + require := require.New(t) + + timestamp := time.Now().Truncate(time.Second) + parentID := ids.GenerateTestID() + height := uint64(1337) + blk, err := NewBanffCommitBlock( + timestamp, + parentID, + height, + ) + require.NoError(err) + + // Make sure the block is initialized + require.NotNil(blk.Bytes()) + + require.Equal(timestamp, blk.Timestamp()) + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} + +func TestNewApricotCommitBlock(t *testing.T) { + require := require.New(t) + + parentID := ids.GenerateTestID() + height := uint64(1337) + blk, err := NewApricotCommitBlock( + parentID, + height, + ) + require.NoError(err) + + // Make sure the block is initialized + require.NotNil(blk.Bytes()) + + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} diff --git a/avalanchego/vms/platformvm/blocks/common_block.go b/avalanchego/vms/platformvm/blocks/common_block.go index 749be832..53e52d26 100644 --- a/avalanchego/vms/platformvm/blocks/common_block.go +++ b/avalanchego/vms/platformvm/blocks/common_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks @@ -10,8 +10,11 @@ import ( // CommonBlock contains fields and methods common to all blocks in this VM. type CommonBlock struct { - PrntID ids.ID `serialize:"true" json:"parentID"` // parent's ID - Hght uint64 `serialize:"true" json:"height"` // This block's height. The genesis block is at height 0. + // parent's ID + PrntID ids.ID `serialize:"true" json:"parentID"` + + // This block's height. The genesis block is at height 0. + Hght uint64 `serialize:"true" json:"height"` id ids.ID bytes []byte diff --git a/avalanchego/vms/platformvm/blocks/executor/acceptor.go b/avalanchego/vms/platformvm/blocks/executor/acceptor.go index d9ea67fe..65a82954 100644 --- a/avalanchego/vms/platformvm/blocks/executor/acceptor.go +++ b/avalanchego/vms/platformvm/blocks/executor/acceptor.go @@ -8,10 +8,13 @@ import ( "go.uber.org/zap" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/window" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/metrics" + "github.com/ava-labs/avalanchego/vms/platformvm/state" ) var _ blocks.Visitor = &acceptor{} @@ -22,87 +25,115 @@ var _ blocks.Visitor = &acceptor{} type acceptor struct { *backend metrics metrics.Metrics - recentlyAccepted *window.Window -} - -// Note that: -// - We don't free the proposal block in this method. It is freed when its child -// is accepted. We need to keep this block's state in memory for its child to -// use. -// - We only update the metrics to reflect this block's acceptance when its -// child is accepted. -// - We don't write this block to state here. That is done when this block's -// child (a CommitBlock or AbortBlock) is accepted. We do this so that in the -// event that the node shuts down, the proposal block is not written to disk -// unless its child is. (The VM's Shutdown method commits the database.) -func (a *acceptor) ProposalBlock(b *blocks.ProposalBlock) error { - blkID := b.ID() - a.ctx.Log.Verbo( + recentlyAccepted window.Window[ids.ID] + bootstrapped *utils.AtomicBool +} + +func (a *acceptor) BanffAbortBlock(b *blocks.BanffAbortBlock) error { + a.ctx.Log.Debug( "accepting block", - zap.String("blockType", "proposal"), - zap.Stringer("blkID", blkID), + zap.String("blockType", "banff abort"), + zap.Stringer("blkID", b.ID()), zap.Uint64("height", b.Height()), zap.Stringer("parentID", b.Parent()), ) - // See comment for [lastAccepted]. - a.backend.lastAccepted = blkID + return a.abortBlock(b) +} + +func (a *acceptor) BanffCommitBlock(b *blocks.BanffCommitBlock) error { + a.ctx.Log.Debug( + "accepting block", + zap.String("blockType", "banff commit"), + zap.Stringer("blkID", b.ID()), + zap.Uint64("height", b.Height()), + zap.Stringer("parentID", b.Parent()), + ) + + return a.commitBlock(b) +} + +func (a *acceptor) BanffProposalBlock(b *blocks.BanffProposalBlock) error { + a.ctx.Log.Debug( + "accepting block", + zap.String("blockType", "banff proposal"), + zap.Stringer("blkID", b.ID()), + zap.Uint64("height", b.Height()), + zap.Stringer("parentID", b.Parent()), + ) + + a.proposalBlock(b) return nil } -func (a *acceptor) AtomicBlock(b *blocks.AtomicBlock) error { - blkID := b.ID() - defer a.free(blkID) +func (a *acceptor) BanffStandardBlock(b *blocks.BanffStandardBlock) error { + a.ctx.Log.Debug( + "accepting block", + zap.String("blockType", "banff standard"), + zap.Stringer("blkID", b.ID()), + zap.Uint64("height", b.Height()), + zap.Stringer("parentID", b.Parent()), + ) + + return a.standardBlock(b) +} - a.ctx.Log.Verbo( +func (a *acceptor) ApricotAbortBlock(b *blocks.ApricotAbortBlock) error { + a.ctx.Log.Debug( "accepting block", - zap.String("blockType", "atomic"), - zap.Stringer("blkID", blkID), + zap.String("blockType", "apricot abort"), + zap.Stringer("blkID", b.ID()), zap.Uint64("height", b.Height()), zap.Stringer("parentID", b.Parent()), ) - if err := a.commonAccept(b); err != nil { - return err - } + return a.abortBlock(b) +} - blkState, ok := a.blkIDToState[blkID] - if !ok { - return fmt.Errorf("couldn't find state of block %s", blkID) - } +func (a *acceptor) ApricotCommitBlock(b *blocks.ApricotCommitBlock) error { + a.ctx.Log.Debug( + "accepting block", + zap.String("blockType", "apricot commit"), + zap.Stringer("blkID", b.ID()), + zap.Uint64("height", b.Height()), + zap.Stringer("parentID", b.Parent()), + ) - // Update the state to reflect the changes made in [onAcceptState]. - blkState.onAcceptState.Apply(a.state) + return a.commitBlock(b) +} - defer a.state.Abort() - batch, err := a.state.CommitBatch() - if err != nil { - return fmt.Errorf( - "failed to commit VM's database for block %s: %w", - blkID, - err, - ) - } +func (a *acceptor) ApricotProposalBlock(b *blocks.ApricotProposalBlock) error { + a.ctx.Log.Debug( + "accepting block", + zap.String("blockType", "apricot proposal"), + zap.Stringer("blkID", b.ID()), + zap.Uint64("height", b.Height()), + zap.Stringer("parentID", b.Parent()), + ) - // Note that this method writes [batch] to the database. - if err := a.ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil { - return fmt.Errorf( - "failed to atomically accept tx %s in block %s: %w", - b.Tx.ID(), - blkID, - err, - ) - } + a.proposalBlock(b) return nil } -func (a *acceptor) StandardBlock(b *blocks.StandardBlock) error { +func (a *acceptor) ApricotStandardBlock(b *blocks.ApricotStandardBlock) error { + a.ctx.Log.Debug( + "accepting block", + zap.String("blockType", "apricot standard"), + zap.Stringer("blkID", b.ID()), + zap.Uint64("height", b.Height()), + zap.Stringer("parentID", b.Parent()), + ) + + return a.standardBlock(b) +} + +func (a *acceptor) ApricotAtomicBlock(b *blocks.ApricotAtomicBlock) error { blkID := b.ID() defer a.free(blkID) - a.ctx.Log.Verbo( + a.ctx.Log.Debug( "accepting block", - zap.String("blockType", "standard"), + zap.String("blockType", "apricot atomic"), zap.Stringer("blkID", blkID), zap.Uint64("height", b.Height()), zap.Stringer("parentID", b.Parent()), @@ -132,74 +163,55 @@ func (a *acceptor) StandardBlock(b *blocks.StandardBlock) error { // Note that this method writes [batch] to the database. if err := a.ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil { - return fmt.Errorf("failed to apply vm's state to shared memory: %w", err) - } - - if onAcceptFunc := blkState.onAcceptFunc; onAcceptFunc != nil { - onAcceptFunc() + return fmt.Errorf( + "failed to atomically accept tx %s in block %s: %w", + b.Tx.ID(), + blkID, + err, + ) } return nil } -func (a *acceptor) CommitBlock(b *blocks.CommitBlock) error { - blkID := b.ID() +func (a *acceptor) abortBlock(b blocks.Block) error { parentID := b.Parent() - a.ctx.Log.Verbo( - "accepting block", - zap.String("blockType", "commit"), - zap.Stringer("blkID", blkID), - zap.Uint64("height", b.Height()), - zap.Stringer("parentID", parentID), - ) - parentState, ok := a.blkIDToState[parentID] if !ok { - return fmt.Errorf("couldn't find state of block %s, parent of %s", parentID, blkID) + return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID) } - // Update metrics if a.bootstrapped.GetValue() { if parentState.initiallyPreferCommit { - a.metrics.MarkOptionVoteWon() - } else { a.metrics.MarkOptionVoteLost() + } else { + a.metrics.MarkOptionVoteWon() } } - return a.acceptOptionBlock(b, parentState.statelessBlock) + return a.optionBlock(b, parentState.statelessBlock) } -func (a *acceptor) AbortBlock(b *blocks.AbortBlock) error { - blkID := b.ID() +func (a *acceptor) commitBlock(b blocks.Block) error { parentID := b.Parent() - a.ctx.Log.Verbo( - "accepting block", - zap.String("blockType", "abort"), - zap.Stringer("blkID", blkID), - zap.Uint64("height", b.Height()), - zap.Stringer("parentID", parentID), - ) - parentState, ok := a.blkIDToState[parentID] if !ok { - return fmt.Errorf("couldn't find state of block %s, parent of %s", parentID, blkID) + return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID) } - // Update metrics if a.bootstrapped.GetValue() { if parentState.initiallyPreferCommit { - a.metrics.MarkOptionVoteLost() - } else { a.metrics.MarkOptionVoteWon() + } else { + a.metrics.MarkOptionVoteLost() } } - return a.acceptOptionBlock(b, parentState.statelessBlock) + return a.optionBlock(b, parentState.statelessBlock) } -func (a *acceptor) acceptOptionBlock(b blocks.Block, parent blocks.Block) error { +func (a *acceptor) optionBlock(b, parent blocks.Block) error { blkID := b.ID() - parentID := b.Parent() + parentID := parent.ID() defer func() { // Note: we assume this block's sibling doesn't @@ -212,6 +224,43 @@ func (a *acceptor) acceptOptionBlock(b blocks.Block, parent blocks.Block) error if err := a.commonAccept(parent); err != nil { return err } + + if err := a.commonAccept(b); err != nil { + return err + } + + blkState, ok := a.blkIDToState[blkID] + if !ok { + return fmt.Errorf("couldn't find state of block %s", blkID) + } + blkState.onAcceptState.Apply(a.state) + return a.state.Commit() +} + +func (a *acceptor) proposalBlock(b blocks.Block) { + // Note that: + // + // * We don't free the proposal block in this method. + // It is freed when its child is accepted. + // We need to keep this block's state in memory for its child to use. + // + // * We only update the metrics to reflect this block's + // acceptance when its child is accepted. + // + // * We don't write this block to state here. + // That is done when this block's child (a CommitBlock or AbortBlock) is accepted. + // We do this so that in the event that the node shuts down, the proposal block + // is not written to disk unless its child is. + // (The VM's Shutdown method commits the database.) + // The snowman.Engine requires that the last committed block is a decision block + + a.backend.lastAccepted = b.ID() +} + +func (a *acceptor) standardBlock(b blocks.Block) error { + blkID := b.ID() + defer a.free(blkID) + if err := a.commonAccept(b); err != nil { return err } @@ -223,20 +272,39 @@ func (a *acceptor) acceptOptionBlock(b blocks.Block, parent blocks.Block) error // Update the state to reflect the changes made in [onAcceptState]. blkState.onAcceptState.Apply(a.state) - return a.state.Commit() + + defer a.state.Abort() + batch, err := a.state.CommitBatch() + if err != nil { + return fmt.Errorf( + "failed to commit VM's database for block %s: %w", + blkID, + err, + ) + } + + // Note that this method writes [batch] to the database. + if err := a.ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil { + return fmt.Errorf("failed to apply vm's state to shared memory: %w", err) + } + + if onAcceptFunc := blkState.onAcceptFunc; onAcceptFunc != nil { + onAcceptFunc() + } + return nil } func (a *acceptor) commonAccept(b blocks.Block) error { blkID := b.ID() + if err := a.metrics.MarkAccepted(b); err != nil { return fmt.Errorf("failed to accept block %s: %w", blkID, err) } - a.backend.lastAccepted = blkID + a.backend.lastAccepted = blkID a.state.SetLastAccepted(blkID) a.state.SetHeight(b.Height()) a.state.AddStatelessBlock(b, choices.Accepted) - a.recentlyAccepted.Add(blkID) return nil } diff --git a/avalanchego/vms/platformvm/blocks/executor/acceptor_test.go b/avalanchego/vms/platformvm/blocks/executor/acceptor_test.go index 026b6c90..fecfefd2 100644 --- a/avalanchego/vms/platformvm/blocks/executor/acceptor_test.go +++ b/avalanchego/vms/platformvm/blocks/executor/acceptor_test.go @@ -35,13 +35,13 @@ func TestAcceptorVisitProposalBlock(t *testing.T) { lastAcceptedID := ids.GenerateTestID() - blk, err := blocks.NewProposalBlock( + blk, err := blocks.NewApricotProposalBlock( lastAcceptedID, 1, &txs.Tx{ Unsigned: &txs.AddDelegatorTx{ // Without the line below, this function will error. - RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{}, }, Creds: []verify.Verifiable{}, }, @@ -65,7 +65,7 @@ func TestAcceptorVisitProposalBlock(t *testing.T) { recentlyAccepted: nil, } - err = acceptor.ProposalBlock(blk) + err = acceptor.ApricotProposalBlock(blk) require.NoError(err) require.Equal(blkID, acceptor.backend.lastAccepted) @@ -99,20 +99,20 @@ func TestAcceptorVisitAtomicBlock(t *testing.T) { }, }, metrics: metrics.Noop, - recentlyAccepted: window.New(window.Config{ + recentlyAccepted: window.New[ids.ID](window.Config{ Clock: &mockable.Clock{}, MaxSize: 1, TTL: time.Hour, }), } - blk, err := blocks.NewAtomicBlock( + blk, err := blocks.NewApricotAtomicBlock( parentID, 1, &txs.Tx{ Unsigned: &txs.AddDelegatorTx{ // Without the line below, this function will error. - RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{}, }, Creds: []verify.Verifiable{}, }, @@ -125,7 +125,7 @@ func TestAcceptorVisitAtomicBlock(t *testing.T) { s.EXPECT().SetHeight(blk.Height()).Times(1) s.EXPECT().AddStatelessBlock(blk, choices.Accepted).Times(1) - err = acceptor.AtomicBlock(blk) + err = acceptor.ApricotAtomicBlock(blk) require.Error(err, "should fail because the block isn't in the state map") // Set [blk]'s state in the map as though it had been verified. @@ -159,7 +159,7 @@ func TestAcceptorVisitAtomicBlock(t *testing.T) { onAcceptState.EXPECT().Apply(s).Times(1) sharedMemory.EXPECT().Apply(atomicRequests, batch).Return(nil).Times(1) - err = acceptor.AtomicBlock(blk) + err = acceptor.ApricotAtomicBlock(blk) require.NoError(err) } @@ -183,21 +183,21 @@ func TestAcceptorVisitStandardBlock(t *testing.T) { }, }, metrics: metrics.Noop, - recentlyAccepted: window.New(window.Config{ + recentlyAccepted: window.New[ids.ID](window.Config{ Clock: &mockable.Clock{}, MaxSize: 1, TTL: time.Hour, }), } - blk, err := blocks.NewStandardBlock( + blk, err := blocks.NewApricotStandardBlock( parentID, 1, []*txs.Tx{ { Unsigned: &txs.AddDelegatorTx{ // Without the line below, this function will error. - RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{}, }, Creds: []verify.Verifiable{}, }, @@ -211,7 +211,7 @@ func TestAcceptorVisitStandardBlock(t *testing.T) { s.EXPECT().SetHeight(blk.Height()).Times(1) s.EXPECT().AddStatelessBlock(blk, choices.Accepted).Times(1) - err = acceptor.StandardBlock(blk) + err = acceptor.ApricotStandardBlock(blk) require.Error(err, "should fail because the block isn't in the state map") // Set [blk]'s state in the map as though it had been verified. @@ -249,7 +249,7 @@ func TestAcceptorVisitStandardBlock(t *testing.T) { onAcceptState.EXPECT().Apply(s).Times(1) sharedMemory.EXPECT().Apply(atomicRequests, batch).Return(nil).Times(1) - err = acceptor.StandardBlock(blk) + err = acceptor.ApricotStandardBlock(blk) require.NoError(err) require.True(calledOnAcceptFunc) require.Equal(blk.ID(), acceptor.backend.lastAccepted) @@ -273,24 +273,21 @@ func TestAcceptorVisitCommitBlock(t *testing.T) { Log: logging.NoLog{}, SharedMemory: sharedMemory, }, - bootstrapped: &utils.AtomicBool{}, }, metrics: metrics.Noop, - recentlyAccepted: window.New(window.Config{ + recentlyAccepted: window.New[ids.ID](window.Config{ Clock: &mockable.Clock{}, MaxSize: 1, TTL: time.Hour, }), + bootstrapped: &utils.AtomicBool{}, } - blk, err := blocks.NewCommitBlock( - parentID, - 1, - ) + blk, err := blocks.NewApricotCommitBlock(parentID, 1 /*height*/) require.NoError(err) - blkID := blk.ID() - err = acceptor.CommitBlock(blk) + blkID := blk.ID() + err = acceptor.ApricotCommitBlock(blk) require.Error(err, "should fail because the block isn't in the state map") // Set [blk]'s state in the map as though it had been verified. @@ -329,7 +326,7 @@ func TestAcceptorVisitCommitBlock(t *testing.T) { // Set expected calls on dependencies. // Make sure the parent is accepted first. gomock.InOrder( - parentStatelessBlk.EXPECT().ID().Return(parentID).Times(1), + parentStatelessBlk.EXPECT().ID().Return(parentID).Times(2), s.EXPECT().SetLastAccepted(parentID).Times(1), parentStatelessBlk.EXPECT().Height().Return(blk.Height()-1).Times(1), s.EXPECT().SetHeight(blk.Height()-1).Times(1), @@ -343,7 +340,7 @@ func TestAcceptorVisitCommitBlock(t *testing.T) { s.EXPECT().Commit().Return(nil).Times(1), ) - err = acceptor.CommitBlock(blk) + err = acceptor.ApricotCommitBlock(blk) require.NoError(err) require.Equal(blk.ID(), acceptor.backend.lastAccepted) } @@ -366,24 +363,21 @@ func TestAcceptorVisitAbortBlock(t *testing.T) { Log: logging.NoLog{}, SharedMemory: sharedMemory, }, - bootstrapped: &utils.AtomicBool{}, }, metrics: metrics.Noop, - recentlyAccepted: window.New(window.Config{ + recentlyAccepted: window.New[ids.ID](window.Config{ Clock: &mockable.Clock{}, MaxSize: 1, TTL: time.Hour, }), + bootstrapped: &utils.AtomicBool{}, } - blk, err := blocks.NewAbortBlock( - parentID, - 1, - ) + blk, err := blocks.NewApricotAbortBlock(parentID, 1 /*height*/) require.NoError(err) - blkID := blk.ID() - err = acceptor.AbortBlock(blk) + blkID := blk.ID() + err = acceptor.ApricotAbortBlock(blk) require.Error(err, "should fail because the block isn't in the state map") // Set [blk]'s state in the map as though it had been verified. @@ -422,7 +416,7 @@ func TestAcceptorVisitAbortBlock(t *testing.T) { // Set expected calls on dependencies. // Make sure the parent is accepted first. gomock.InOrder( - parentStatelessBlk.EXPECT().ID().Return(parentID).Times(1), + parentStatelessBlk.EXPECT().ID().Return(parentID).Times(2), s.EXPECT().SetLastAccepted(parentID).Times(1), parentStatelessBlk.EXPECT().Height().Return(blk.Height()-1).Times(1), s.EXPECT().SetHeight(blk.Height()-1).Times(1), @@ -436,7 +430,7 @@ func TestAcceptorVisitAbortBlock(t *testing.T) { s.EXPECT().Commit().Return(nil).Times(1), ) - err = acceptor.AbortBlock(blk) + err = acceptor.ApricotAbortBlock(blk) require.NoError(err) require.Equal(blk.ID(), acceptor.backend.lastAccepted) } diff --git a/avalanchego/vms/platformvm/blocks/executor/backend.go b/avalanchego/vms/platformvm/blocks/executor/backend.go index 9a3b295a..5402da18 100644 --- a/avalanchego/vms/platformvm/blocks/executor/backend.go +++ b/avalanchego/vms/platformvm/blocks/executor/backend.go @@ -4,9 +4,10 @@ package executor import ( + "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" @@ -28,8 +29,7 @@ type backend struct { blkIDToState map[ids.ID]*blockState state state.State - ctx *snow.Context - bootstrapped *utils.AtomicBool + ctx *snow.Context } func (b *backend) GetState(blkID ids.ID) (state.Chain, bool) { @@ -47,6 +47,22 @@ func (b *backend) GetState(blkID ids.ID) (state.Chain, bool) { return b.state, blkID == b.state.GetLastAccepted() } +func (b *backend) getOnAbortState(blkID ids.ID) (state.Diff, bool) { + state, ok := b.blkIDToState[blkID] + if !ok || state.onAbortState == nil { + return nil, false + } + return state.onAbortState, true +} + +func (b *backend) getOnCommitState(blkID ids.ID) (state.Diff, bool) { + state, ok := b.blkIDToState[blkID] + if !ok || state.onCommitState == nil { + return nil, false + } + return state.onCommitState, true +} + func (b *backend) GetBlock(blkID ids.ID) (blocks.Block, error) { // See if the block is in memory. if blk, ok := b.blkIDToState[blkID]; ok { @@ -64,3 +80,18 @@ func (b *backend) LastAccepted() ids.ID { func (b *backend) free(blkID ids.ID) { delete(b.blkIDToState, blkID) } + +func (b *backend) getTimestamp(blkID ids.ID) time.Time { + // Check if the block is processing. + // If the block is processing, then we are guaranteed to have populated its + // timestamp in its state. + if blkState, ok := b.blkIDToState[blkID]; ok { + return blkState.timestamp + } + + // The block isn't processing. + // According to the snowman.Block interface, the last accepted + // block is the only accepted block that must return a correct timestamp, + // so we just return the chain time. + return b.state.GetTimestamp() +} diff --git a/avalanchego/vms/platformvm/blocks/executor/backend_test.go b/avalanchego/vms/platformvm/blocks/executor/backend_test.go new file mode 100644 index 00000000..0d7ce90d --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/executor/backend_test.go @@ -0,0 +1,162 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/state" +) + +func TestGetState(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + var ( + mockState = state.NewMockState(ctrl) + onAcceptState = state.NewMockDiff(ctrl) + blkID1 = ids.GenerateTestID() + blkID2 = ids.GenerateTestID() + b = &backend{ + state: mockState, + blkIDToState: map[ids.ID]*blockState{ + blkID1: { + onAcceptState: onAcceptState, + }, + blkID2: {}, + }, + } + ) + + { + // Case: block is in the map and onAcceptState isn't nil. + gotState, ok := b.GetState(blkID1) + require.True(ok) + require.Equal(onAcceptState, gotState) + } + + { + // Case: block is in the map and onAcceptState is nil. + _, ok := b.GetState(blkID2) + require.False(ok) + } + + { + // Case: block is not in the map and block isn't last accepted. + mockState.EXPECT().GetLastAccepted().Return(ids.GenerateTestID()) + _, ok := b.GetState(ids.GenerateTestID()) + require.False(ok) + } + + { + // Case: block is not in the map and block is last accepted. + blkID := ids.GenerateTestID() + mockState.EXPECT().GetLastAccepted().Return(blkID) + gotState, ok := b.GetState(blkID) + require.True(ok) + require.Equal(mockState, gotState) + } +} + +func TestBackendGetBlock(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + var ( + blkID1 = ids.GenerateTestID() + statelessBlk = blocks.NewMockBlock(ctrl) + state = state.NewMockState(ctrl) + b = &backend{ + state: state, + blkIDToState: map[ids.ID]*blockState{ + blkID1: { + statelessBlock: statelessBlk, + }, + }, + } + ) + + { + // Case: block is in the map. + gotBlk, err := b.GetBlock(blkID1) + require.Nil(err) + require.Equal(statelessBlk, gotBlk) + } + + { + // Case: block isn't in the map or database. + blkID := ids.GenerateTestID() + state.EXPECT().GetStatelessBlock(blkID).Return(nil, choices.Unknown, database.ErrNotFound) + _, err := b.GetBlock(blkID) + require.Equal(database.ErrNotFound, err) + } + + { + // Case: block isn't in the map and is in database. + blkID := ids.GenerateTestID() + state.EXPECT().GetStatelessBlock(blkID).Return(statelessBlk, choices.Accepted, nil) + gotBlk, err := b.GetBlock(blkID) + require.NoError(err) + require.Equal(statelessBlk, gotBlk) + } +} + +func TestGetTimestamp(t *testing.T) { + type test struct { + name string + backendF func(*gomock.Controller) *backend + expectedTimestamp time.Time + } + + blkID := ids.GenerateTestID() + tests := []test{ + { + name: "block is in map", + backendF: func(ctrl *gomock.Controller) *backend { + return &backend{ + blkIDToState: map[ids.ID]*blockState{ + blkID: { + timestamp: time.Unix(1337, 0), + }, + }, + } + }, + expectedTimestamp: time.Unix(1337, 0), + }, + { + name: "block isn't map", + backendF: func(ctrl *gomock.Controller) *backend { + state := state.NewMockState(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(1337, 0)) + return &backend{ + state: state, + } + }, + expectedTimestamp: time.Unix(1337, 0), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + backend := tt.backendF(ctrl) + gotTimestamp := backend.getTimestamp(blkID) + require.Equal(tt.expectedTimestamp, gotTimestamp) + }) + } +} diff --git a/avalanchego/vms/platformvm/blocks/executor/block.go b/avalanchego/vms/platformvm/blocks/executor/block.go index a814a024..b87131ca 100644 --- a/avalanchego/vms/platformvm/blocks/executor/block.go +++ b/avalanchego/vms/platformvm/blocks/executor/block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -7,6 +7,9 @@ import ( "fmt" "time" + "go.uber.org/zap" + + "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" @@ -24,6 +27,12 @@ type Block struct { } func (b *Block) Verify() error { + blkID := b.ID() + if _, ok := b.manager.blkIDToState[blkID]; ok { + // This block has already been verified. + return nil + } + return b.Visit(b.manager.verifier) } @@ -40,73 +49,53 @@ func (b *Block) Status() choices.Status { // If this block is an accepted Proposal block with no accepted children, it // will be in [blkIDToState], but we should return accepted, not processing, // so we do this check. - if b.manager.backend.lastAccepted == blkID { + if b.manager.lastAccepted == blkID { return choices.Accepted } // Check if the block is in memory. If so, it's processing. - if _, ok := b.manager.backend.blkIDToState[blkID]; ok { + if _, ok := b.manager.blkIDToState[blkID]; ok { return choices.Processing } // Block isn't in memory. Check in the database. - if _, status, err := b.manager.state.GetStatelessBlock(blkID); err == nil { + _, status, err := b.manager.state.GetStatelessBlock(blkID) + switch err { + case nil: return status + + case database.ErrNotFound: + // choices.Unknown means we don't have the bytes of the block. + // In this case, we do, so we return choices.Processing. + return choices.Processing + + default: + // TODO: correctly report this error to the consensus engine. + b.manager.ctx.Log.Error( + "dropping unhandled database error", + zap.Error(err), + ) + return choices.Processing } - // choices.Unknown means we don't have the bytes of the block. - // In this case, we do, so we return choices.Processing. - return choices.Processing } func (b *Block) Timestamp() time.Time { - // If this is the last accepted block and the block was loaded from disk - // since it was accepted, then the timestamp wouldn't be set correctly. So, - // we explicitly return the chain time. - // Check if the block is processing. - if blkState, ok := b.manager.blkIDToState[b.ID()]; ok { - return blkState.timestamp - } - // The block isn't processing. - // According to the snowman.Block interface, the last accepted - // block is the only accepted block that must return a correct timestamp, - // so we just return the chain time. - return b.manager.state.GetTimestamp() + return b.manager.getTimestamp(b.ID()) } func (b *Block) Options() ([2]snowman.Block, error) { - if _, ok := b.Block.(*blocks.ProposalBlock); !ok { - return [2]snowman.Block{}, snowman.ErrNotOracle + options := options{} + if err := b.Block.Visit(&options); err != nil { + return [2]snowman.Block{}, err } - blkID := b.ID() - nextHeight := b.Height() + 1 - - statelessCommitBlk, err := blocks.NewCommitBlock( - blkID, - nextHeight, - ) - if err != nil { - return [2]snowman.Block{}, fmt.Errorf( - "failed to create commit block: %w", - err, - ) - } - commitBlock := b.manager.NewBlock(statelessCommitBlk) - - statelessAbortBlk, err := blocks.NewAbortBlock( - blkID, - nextHeight, - ) - if err != nil { - return [2]snowman.Block{}, fmt.Errorf( - "failed to create abort block: %w", - err, - ) - } - abortBlock := b.manager.NewBlock(statelessAbortBlk) + commitBlock := b.manager.NewBlock(options.commitBlock) + abortBlock := b.manager.NewBlock(options.abortBlock) - blkState, ok := b.manager.backend.blkIDToState[blkID] + blkID := b.ID() + blkState, ok := b.manager.blkIDToState[blkID] if !ok { return [2]snowman.Block{}, fmt.Errorf("block %s state not found", blkID) } + if blkState.initiallyPreferCommit { return [2]snowman.Block{commitBlock, abortBlock}, nil } diff --git a/avalanchego/vms/platformvm/blocks/executor/block_test.go b/avalanchego/vms/platformvm/blocks/executor/block_test.go new file mode 100644 index 00000000..25a32179 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/executor/block_test.go @@ -0,0 +1,256 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "testing" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/state" +) + +func TestStatus(t *testing.T) { + type test struct { + name string + blockF func(*gomock.Controller) *Block + expectedStatus choices.Status + } + + tests := []test{ + { + name: "last accepted", + blockF: func(ctrl *gomock.Controller) *Block { + blkID := ids.GenerateTestID() + statelessBlk := blocks.NewMockBlock(ctrl) + statelessBlk.EXPECT().ID().Return(blkID) + + manager := &manager{ + backend: &backend{ + lastAccepted: blkID, + }, + } + + return &Block{ + Block: statelessBlk, + manager: manager, + } + }, + expectedStatus: choices.Accepted, + }, + { + name: "processing", + blockF: func(ctrl *gomock.Controller) *Block { + blkID := ids.GenerateTestID() + statelessBlk := blocks.NewMockBlock(ctrl) + statelessBlk.EXPECT().ID().Return(blkID) + + manager := &manager{ + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + blkID: {}, + }, + }, + } + return &Block{ + Block: statelessBlk, + manager: manager, + } + }, + expectedStatus: choices.Processing, + }, + { + name: "in database", + blockF: func(ctrl *gomock.Controller) *Block { + blkID := ids.GenerateTestID() + statelessBlk := blocks.NewMockBlock(ctrl) + statelessBlk.EXPECT().ID().Return(blkID) + + state := state.NewMockState(ctrl) + state.EXPECT().GetStatelessBlock(blkID).Return(statelessBlk, choices.Accepted, nil) + + manager := &manager{ + backend: &backend{ + state: state, + }, + } + return &Block{ + Block: statelessBlk, + manager: manager, + } + }, + expectedStatus: choices.Accepted, + }, + { + name: "not in map or database", + blockF: func(ctrl *gomock.Controller) *Block { + blkID := ids.GenerateTestID() + statelessBlk := blocks.NewMockBlock(ctrl) + statelessBlk.EXPECT().ID().Return(blkID) + + state := state.NewMockState(ctrl) + state.EXPECT().GetStatelessBlock(blkID).Return(nil, choices.Unknown, database.ErrNotFound) + + manager := &manager{ + backend: &backend{ + state: state, + }, + } + return &Block{ + Block: statelessBlk, + manager: manager, + } + }, + expectedStatus: choices.Processing, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + blk := tt.blockF(ctrl) + require.Equal(tt.expectedStatus, blk.Status()) + }) + } +} + +func TestBlockOptions(t *testing.T) { + type test struct { + name string + blkF func() *Block + expectedPreferenceType blocks.Block + expectedErr error + } + + tests := []test{ + { + name: "apricot proposal block; commit preferred", + blkF: func() *Block { + innerBlk := &blocks.ApricotProposalBlock{} + blkID := innerBlk.ID() + + manager := &manager{ + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + blkID: { + proposalBlockState: proposalBlockState{ + initiallyPreferCommit: true, + }, + }, + }, + }, + } + + return &Block{ + Block: innerBlk, + manager: manager, + } + }, + expectedPreferenceType: &blocks.ApricotCommitBlock{}, + }, + { + name: "apricot proposal block; abort preferred", + blkF: func() *Block { + innerBlk := &blocks.ApricotProposalBlock{} + blkID := innerBlk.ID() + + manager := &manager{ + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + blkID: {}, + }, + }, + } + + return &Block{ + Block: innerBlk, + manager: manager, + } + }, + expectedPreferenceType: &blocks.ApricotAbortBlock{}, + }, + { + name: "banff proposal block; commit preferred", + blkF: func() *Block { + innerBlk := &blocks.BanffProposalBlock{} + blkID := innerBlk.ID() + + manager := &manager{ + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + blkID: { + proposalBlockState: proposalBlockState{ + initiallyPreferCommit: true, + }, + }, + }, + }, + } + + return &Block{ + Block: innerBlk, + manager: manager, + } + }, + expectedPreferenceType: &blocks.BanffCommitBlock{}, + }, + { + name: "banff proposal block; abort preferred", + blkF: func() *Block { + innerBlk := &blocks.BanffProposalBlock{} + blkID := innerBlk.ID() + + manager := &manager{ + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + blkID: {}, + }, + }, + } + + return &Block{ + Block: innerBlk, + manager: manager, + } + }, + expectedPreferenceType: &blocks.BanffAbortBlock{}, + }, + { + name: "non oracle block", + blkF: func() *Block { + return &Block{ + Block: &blocks.BanffStandardBlock{}, + manager: &manager{}, + } + }, + expectedErr: snowman.ErrNotOracle, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + blk := tt.blkF() + options, err := blk.Options() + if tt.expectedErr != nil { + require.ErrorIs(err, tt.expectedErr) + return + } + require.IsType(tt.expectedPreferenceType, options[0].(*Block).Block) + }) + } +} diff --git a/avalanchego/vms/platformvm/blocks/executor/helpers_test.go b/avalanchego/vms/platformvm/blocks/executor/helpers_test.go new file mode 100644 index 00000000..d5913039 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/executor/helpers_test.go @@ -0,0 +1,529 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/ava-labs/avalanchego/chains" + "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/codec/linearcodec" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/database/prefixdb" + "github.com/ava-labs/avalanchego/database/versiondb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/uptime" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/utils/formatting" + "github.com/ava-labs/avalanchego/utils/formatting/address" + "github.com/ava-labs/avalanchego/utils/json" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/utils/window" + "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/avalanchego/version" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/api" + "github.com/ava-labs/avalanchego/vms/platformvm/config" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/metrics" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/status" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" + "github.com/ava-labs/avalanchego/vms/platformvm/utxo" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + db_manager "github.com/ava-labs/avalanchego/database/manager" + p_tx_builder "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" +) + +const ( + pending stakerStatus = iota + current + + testNetworkID = 10 // To be used in tests + defaultWeight = 10000 + maxRecentlyAcceptedWindowSize = 256 + recentlyAcceptedWindowTTL = 5 * time.Minute +) + +var ( + _ mempool.BlockTimer = &environment{} + + defaultMinStakingDuration = 24 * time.Hour + defaultMaxStakingDuration = 365 * 24 * time.Hour + defaultGenesisTime = time.Date(1997, 1, 1, 0, 0, 0, 0, time.UTC) + defaultValidateStartTime = defaultGenesisTime + defaultValidateEndTime = defaultValidateStartTime.Add(10 * defaultMinStakingDuration) + defaultMinValidatorStake = 5 * units.MilliAvax + defaultBalance = 100 * defaultMinValidatorStake + preFundedKeys = crypto.BuildTestKeys() + avaxAssetID = ids.ID{'y', 'e', 'e', 't'} + defaultTxFee = uint64(100) + xChainID = ids.Empty.Prefix(0) + cChainID = ids.Empty.Prefix(1) + + genesisBlkID ids.ID + testSubnet1 *txs.Tx +) + +type stakerStatus uint + +type staker struct { + nodeID ids.NodeID + rewardAddress ids.ShortID + startTime, endTime time.Time +} + +type test struct { + description string + stakers []staker + subnetStakers []staker + advanceTimeTo []time.Time + expectedStakers map[ids.NodeID]stakerStatus + expectedSubnetStakers map[ids.NodeID]stakerStatus +} + +type environment struct { + blkManager Manager + mempool mempool.Mempool + sender *common.SenderTest + + isBootstrapped *utils.AtomicBool + config *config.Config + clk *mockable.Clock + baseDB *versiondb.Database + ctx *snow.Context + fx fx.Fx + state state.State + mockedState *state.MockState + atomicUTXOs avax.AtomicUTXOManager + uptimes uptime.Manager + utxosHandler utxo.Handler + txBuilder p_tx_builder.Builder + backend *executor.Backend +} + +func (t *environment) ResetBlockTimer() { + // dummy call, do nothing for now +} + +// TODO snLookup currently duplicated in vm_test.go. Consider removing duplication +type snLookup struct { + chainsToSubnet map[ids.ID]ids.ID +} + +func (sn *snLookup) SubnetID(chainID ids.ID) (ids.ID, error) { + subnetID, ok := sn.chainsToSubnet[chainID] + if !ok { + return ids.ID{}, errors.New("") + } + return subnetID, nil +} + +func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { + res := &environment{ + isBootstrapped: &utils.AtomicBool{}, + config: defaultConfig(), + clk: defaultClock(), + } + res.isBootstrapped.SetValue(true) + + baseDBManager := db_manager.NewMemDB(version.Semantic1_0_0) + res.baseDB = versiondb.New(baseDBManager.Current().Database) + res.ctx = defaultCtx(res.baseDB) + res.fx = defaultFx(res.clk, res.ctx.Log, res.isBootstrapped.GetValue()) + + rewardsCalc := reward.NewCalculator(res.config.RewardConfig) + res.atomicUTXOs = avax.NewAtomicUTXOManager(res.ctx.SharedMemory, txs.Codec) + + if ctrl == nil { + res.state = defaultState(res.config, res.ctx, res.baseDB, rewardsCalc) + res.uptimes = uptime.NewManager(res.state) + res.utxosHandler = utxo.NewHandler(res.ctx, res.clk, res.state, res.fx) + res.txBuilder = p_tx_builder.New( + res.ctx, + res.config, + res.clk, + res.fx, + res.state, + res.atomicUTXOs, + res.utxosHandler, + ) + } else { + genesisBlkID = ids.GenerateTestID() + res.mockedState = state.NewMockState(ctrl) + res.uptimes = uptime.NewManager(res.mockedState) + res.utxosHandler = utxo.NewHandler(res.ctx, res.clk, res.mockedState, res.fx) + res.txBuilder = p_tx_builder.New( + res.ctx, + res.config, + res.clk, + res.fx, + res.mockedState, + res.atomicUTXOs, + res.utxosHandler, + ) + + // setup expectations strictly needed for environment creation + res.mockedState.EXPECT().GetLastAccepted().Return(genesisBlkID).Times(1) + } + + res.backend = &executor.Backend{ + Config: res.config, + Ctx: res.ctx, + Clk: res.clk, + Bootstrapped: res.isBootstrapped, + Fx: res.fx, + FlowChecker: res.utxosHandler, + Uptimes: res.uptimes, + Rewards: rewardsCalc, + } + + registerer := prometheus.NewRegistry() + window := window.New[ids.ID]( + window.Config{ + Clock: res.clk, + MaxSize: maxRecentlyAcceptedWindowSize, + TTL: recentlyAcceptedWindowTTL, + }, + ) + res.sender = &common.SenderTest{T: t} + + metrics := metrics.Noop + + var err error + res.mempool, err = mempool.NewMempool("mempool", registerer, res) + if err != nil { + panic(fmt.Errorf("failed to create mempool: %w", err)) + } + + if ctrl == nil { + res.blkManager = NewManager( + res.mempool, + metrics, + res.state, + res.backend, + window, + ) + addSubnet(res) + } else { + res.blkManager = NewManager( + res.mempool, + metrics, + res.mockedState, + res.backend, + window, + ) + // we do not add any subnet to state, since we can mock + // whatever we need + } + + return res +} + +func addSubnet(env *environment) { + // Create a subnet + var err error + testSubnet1, err = env.txBuilder.NewCreateSubnetTx( + 2, // threshold; 2 sigs from keys[0], keys[1], keys[2] needed to add validator to this subnet + []ids.ShortID{ // control keys + preFundedKeys[0].PublicKey().Address(), + preFundedKeys[1].PublicKey().Address(), + preFundedKeys[2].PublicKey().Address(), + }, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + preFundedKeys[0].PublicKey().Address(), + ) + if err != nil { + panic(err) + } + + // store it + genesisID := env.state.GetLastAccepted() + stateDiff, err := state.NewDiff(genesisID, env.blkManager) + if err != nil { + panic(err) + } + + executor := executor.StandardTxExecutor{ + Backend: env.backend, + State: stateDiff, + Tx: testSubnet1, + } + err = testSubnet1.Unsigned.Visit(&executor) + if err != nil { + panic(err) + } + + stateDiff.AddTx(testSubnet1, status.Committed) + stateDiff.Apply(env.state) +} + +func defaultState( + cfg *config.Config, + ctx *snow.Context, + db database.Database, + rewards reward.Calculator, +) state.State { + genesisBytes := buildGenesisTest(ctx) + state, err := state.New( + db, + genesisBytes, + prometheus.NewRegistry(), + cfg, + ctx, + metrics.Noop, + rewards, + ) + if err != nil { + panic(err) + } + + // persist and reload to init a bunch of in-memory stuff + state.SetHeight(0) + if err := state.Commit(); err != nil { + panic(err) + } + state.SetHeight( /*height*/ 0) + if err := state.Commit(); err != nil { + panic(err) + } + genesisBlkID = state.GetLastAccepted() + return state +} + +func defaultCtx(db database.Database) *snow.Context { + ctx := snow.DefaultContextTest() + ctx.NetworkID = 10 + ctx.XChainID = xChainID + ctx.AVAXAssetID = avaxAssetID + + atomicDB := prefixdb.New([]byte{1}, db) + m := atomic.NewMemory(atomicDB) + + ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) + + ctx.SNLookup = &snLookup{ + chainsToSubnet: map[ids.ID]ids.ID{ + constants.PlatformChainID: constants.PrimaryNetworkID, + xChainID: constants.PrimaryNetworkID, + cChainID: constants.PrimaryNetworkID, + }, + } + + return ctx +} + +func defaultConfig() *config.Config { + return &config.Config{ + Chains: chains.MockManager{}, + UptimeLockedCalculator: uptime.NewLockedCalculator(), + Validators: validators.NewManager(), + TxFee: defaultTxFee, + CreateSubnetTxFee: 100 * defaultTxFee, + CreateBlockchainTxFee: 100 * defaultTxFee, + MinValidatorStake: 5 * units.MilliAvax, + MaxValidatorStake: 500 * units.MilliAvax, + MinDelegatorStake: 1 * units.MilliAvax, + MinStakeDuration: defaultMinStakingDuration, + MaxStakeDuration: defaultMaxStakingDuration, + RewardConfig: reward.Config{ + MaxConsumptionRate: .12 * reward.PercentDenominator, + MinConsumptionRate: .10 * reward.PercentDenominator, + MintingPeriod: 365 * 24 * time.Hour, + SupplyCap: 720 * units.MegaAvax, + }, + ApricotPhase3Time: defaultValidateEndTime, + ApricotPhase5Time: defaultValidateEndTime, + BanffTime: mockable.MaxTime, + } +} + +func defaultClock() *mockable.Clock { + clk := mockable.Clock{} + clk.Set(defaultGenesisTime) + return &clk +} + +type fxVMInt struct { + registry codec.Registry + clk *mockable.Clock + log logging.Logger +} + +func (fvi *fxVMInt) CodecRegistry() codec.Registry { return fvi.registry } +func (fvi *fxVMInt) Clock() *mockable.Clock { return fvi.clk } +func (fvi *fxVMInt) Logger() logging.Logger { return fvi.log } + +func defaultFx(clk *mockable.Clock, log logging.Logger, isBootstrapped bool) fx.Fx { + fxVMInt := &fxVMInt{ + registry: linearcodec.NewDefault(), + clk: clk, + log: log, + } + res := &secp256k1fx.Fx{} + if err := res.Initialize(fxVMInt); err != nil { + panic(err) + } + if isBootstrapped { + if err := res.Bootstrapped(); err != nil { + panic(err) + } + } + return res +} + +func buildGenesisTest(ctx *snow.Context) []byte { + genesisUTXOs := make([]api.UTXO, len(preFundedKeys)) + hrp := constants.NetworkIDToHRP[testNetworkID] + for i, key := range preFundedKeys { + id := key.PublicKey().Address() + addr, err := address.FormatBech32(hrp, id.Bytes()) + if err != nil { + panic(err) + } + genesisUTXOs[i] = api.UTXO{ + Amount: json.Uint64(defaultBalance), + Address: addr, + } + } + + genesisValidators := make([]api.PermissionlessValidator, len(preFundedKeys)) + for i, key := range preFundedKeys { + nodeID := ids.NodeID(key.PublicKey().Address()) + addr, err := address.FormatBech32(hrp, nodeID.Bytes()) + if err != nil { + panic(err) + } + genesisValidators[i] = api.PermissionlessValidator{ + Staker: api.Staker{ + StartTime: json.Uint64(defaultValidateStartTime.Unix()), + EndTime: json.Uint64(defaultValidateEndTime.Unix()), + NodeID: nodeID, + }, + RewardOwner: &api.Owner{ + Threshold: 1, + Addresses: []string{addr}, + }, + Staked: []api.UTXO{{ + Amount: json.Uint64(defaultWeight), + Address: addr, + }}, + DelegationFee: reward.PercentDenominator, + } + } + + buildGenesisArgs := api.BuildGenesisArgs{ + NetworkID: json.Uint32(testNetworkID), + AvaxAssetID: ctx.AVAXAssetID, + UTXOs: genesisUTXOs, + Validators: genesisValidators, + Chains: nil, + Time: json.Uint64(defaultGenesisTime.Unix()), + InitialSupply: json.Uint64(360 * units.MegaAvax), + Encoding: formatting.Hex, + } + + buildGenesisResponse := api.BuildGenesisReply{} + platformvmSS := api.StaticService{} + if err := platformvmSS.BuildGenesis(nil, &buildGenesisArgs, &buildGenesisResponse); err != nil { + panic(fmt.Errorf("problem while building platform chain's genesis state: %v", err)) + } + + genesisBytes, err := formatting.Decode(buildGenesisResponse.Encoding, buildGenesisResponse.Bytes) + if err != nil { + panic(err) + } + + return genesisBytes +} + +func shutdownEnvironment(t *environment) error { + if t.mockedState != nil { + // state is mocked, nothing to do here + return nil + } + + if t.isBootstrapped.GetValue() { + primaryValidatorSet, exist := t.config.Validators.GetValidators(constants.PrimaryNetworkID) + if !exist { + return errors.New("no default subnet validators") + } + primaryValidators := primaryValidatorSet.List() + + validatorIDs := make([]ids.NodeID, len(primaryValidators)) + for i, vdr := range primaryValidators { + validatorIDs[i] = vdr.ID() + } + + if err := t.uptimes.Shutdown(validatorIDs); err != nil { + return err + } + if err := t.state.Commit(); err != nil { + return err + } + } + + errs := wrappers.Errs{} + if t.state != nil { + errs.Add(t.state.Close()) + } + errs.Add(t.baseDB.Close()) + return errs.Err +} + +func addPendingValidator( + env *environment, + startTime time.Time, + endTime time.Time, + nodeID ids.NodeID, + rewardAddress ids.ShortID, + keys []*crypto.PrivateKeySECP256K1R, +) (*txs.Tx, error) { + addPendingValidatorTx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, + uint64(startTime.Unix()), + uint64(endTime.Unix()), + nodeID, + rewardAddress, + reward.PercentDenominator, + keys, + ids.ShortEmpty, + ) + if err != nil { + return nil, err + } + + staker := state.NewPendingStaker( + addPendingValidatorTx.ID(), + addPendingValidatorTx.Unsigned.(*txs.AddValidatorTx), + ) + + env.state.PutPendingValidator(staker) + env.state.AddTx(addPendingValidatorTx, status.Committed) + dummyHeight := uint64(1) + env.state.SetHeight(dummyHeight) + if err := env.state.Commit(); err != nil { + return nil, err + } + return addPendingValidatorTx, nil +} diff --git a/avalanchego/vms/platformvm/blocks/executor/manager.go b/avalanchego/vms/platformvm/blocks/executor/manager.go index 11e2ac94..c1802763 100644 --- a/avalanchego/vms/platformvm/blocks/executor/manager.go +++ b/avalanchego/vms/platformvm/blocks/executor/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -22,6 +22,7 @@ type Manager interface { // Returns the ID of the most recently accepted block. LastAccepted() ids.ID GetBlock(blkID ids.ID) (snowman.Block, error) + GetStatelessBlock(blkID ids.ID) (blocks.Block, error) NewBlock(blocks.Block) snowman.Block } @@ -30,13 +31,12 @@ func NewManager( metrics metrics.Metrics, s state.State, txExecutorBackend *executor.Backend, - recentlyAccepted *window.Window, + recentlyAccepted window.Window[ids.ID], ) Manager { backend := &backend{ Mempool: mempool, lastAccepted: s.GetLastAccepted(), state: s, - bootstrapped: txExecutorBackend.Bootstrapped, ctx: txExecutorBackend.Ctx, blkIDToState: map[ids.ID]*blockState{}, } @@ -51,6 +51,7 @@ func NewManager( backend: backend, metrics: metrics, recentlyAccepted: recentlyAccepted, + bootstrapped: txExecutorBackend.Bootstrapped, }, rejector: &rejector{backend: backend}, } @@ -71,6 +72,10 @@ func (m *manager) GetBlock(blkID ids.ID) (snowman.Block, error) { return m.NewBlock(blk), nil } +func (m *manager) GetStatelessBlock(blkID ids.ID) (blocks.Block, error) { + return m.backend.GetBlock(blkID) +} + func (m *manager) NewBlock(blk blocks.Block) snowman.Block { return &Block{ manager: m, diff --git a/avalanchego/vms/platformvm/blocks/executor/manager_test.go b/avalanchego/vms/platformvm/blocks/executor/manager_test.go index 743b7490..e2345bc3 100644 --- a/avalanchego/vms/platformvm/blocks/executor/manager_test.go +++ b/avalanchego/vms/platformvm/blocks/executor/manager_test.go @@ -22,7 +22,7 @@ func TestGetBlock(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - statelessBlk, err := blocks.NewCommitBlock(ids.GenerateTestID(), 2) + statelessBlk, err := blocks.NewApricotCommitBlock(ids.GenerateTestID() /*parent*/, 2 /*height*/) require.NoError(err) state := state.NewMockState(ctrl) manager := &manager{ diff --git a/avalanchego/vms/platformvm/blocks/executor/options.go b/avalanchego/vms/platformvm/blocks/executor/options.go new file mode 100644 index 00000000..e455a7b7 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/executor/options.go @@ -0,0 +1,95 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "fmt" + + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" +) + +var _ blocks.Visitor = &verifier{} + +// options supports build new option blocks +type options struct { + // outputs populated by this struct's methods: + commitBlock blocks.Block + abortBlock blocks.Block +} + +func (*options) BanffAbortBlock(*blocks.BanffAbortBlock) error { + return snowman.ErrNotOracle +} + +func (*options) BanffCommitBlock(*blocks.BanffCommitBlock) error { + return snowman.ErrNotOracle +} + +func (o *options) BanffProposalBlock(b *blocks.BanffProposalBlock) error { + timestamp := b.Timestamp() + blkID := b.ID() + nextHeight := b.Height() + 1 + + var err error + o.commitBlock, err = blocks.NewBanffCommitBlock(timestamp, blkID, nextHeight) + if err != nil { + return fmt.Errorf( + "failed to create commit block: %w", + err, + ) + } + + o.abortBlock, err = blocks.NewBanffAbortBlock(timestamp, blkID, nextHeight) + if err != nil { + return fmt.Errorf( + "failed to create abort block: %w", + err, + ) + } + return nil +} + +func (*options) BanffStandardBlock(*blocks.BanffStandardBlock) error { + return snowman.ErrNotOracle +} + +func (*options) ApricotAbortBlock(*blocks.ApricotAbortBlock) error { + return snowman.ErrNotOracle +} + +func (*options) ApricotCommitBlock(*blocks.ApricotCommitBlock) error { + return snowman.ErrNotOracle +} + +func (o *options) ApricotProposalBlock(b *blocks.ApricotProposalBlock) error { + blkID := b.ID() + nextHeight := b.Height() + 1 + + var err error + o.commitBlock, err = blocks.NewApricotCommitBlock(blkID, nextHeight) + if err != nil { + return fmt.Errorf( + "failed to create commit block: %w", + err, + ) + } + + o.abortBlock, err = blocks.NewApricotAbortBlock(blkID, nextHeight) + if err != nil { + return fmt.Errorf( + "failed to create abort block: %w", + err, + ) + } + return nil +} + +func (*options) ApricotStandardBlock(*blocks.ApricotStandardBlock) error { + return snowman.ErrNotOracle +} + +func (*options) ApricotAtomicBlock(*blocks.ApricotAtomicBlock) error { + return snowman.ErrNotOracle +} diff --git a/avalanchego/vms/platformvm/blocks/executor/options_test.go b/avalanchego/vms/platformvm/blocks/executor/options_test.go new file mode 100644 index 00000000..0313e89b --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/executor/options_test.go @@ -0,0 +1,33 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" +) + +func TestOptionsUnexpectedBlockType(t *testing.T) { + tests := []blocks.Block{ + &blocks.BanffAbortBlock{}, + &blocks.BanffCommitBlock{}, + &blocks.BanffStandardBlock{}, + &blocks.ApricotAbortBlock{}, + &blocks.ApricotCommitBlock{}, + &blocks.ApricotStandardBlock{}, + &blocks.ApricotAtomicBlock{}, + } + + for _, blk := range tests { + t.Run(fmt.Sprintf("%T", blk), func(t *testing.T) { + err := blk.Visit(&options{}) + require.ErrorIs(t, err, snowman.ErrNotOracle) + }) + } +} diff --git a/avalanchego/vms/platformvm/blocks/executor/proposal_block_test.go b/avalanchego/vms/platformvm/blocks/executor/proposal_block_test.go new file mode 100644 index 00000000..9e80e8f2 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/executor/proposal_block_test.go @@ -0,0 +1,1284 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "fmt" + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/status" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +func TestApricotProposalBlockTimeVerification(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + env := newEnvironment(t, ctrl) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + + // create apricotParentBlk. It's a standard one for simplicity + parentHeight := uint64(2022) + + apricotParentBlk, err := blocks.NewApricotStandardBlock( + ids.Empty, // does not matter + parentHeight, + nil, // txs do not matter in this test + ) + require.NoError(err) + parentID := apricotParentBlk.ID() + + // store parent block, with relevant quantities + onParentAccept := state.NewMockDiff(ctrl) + env.blkManager.(*manager).blkIDToState[parentID] = &blockState{ + statelessBlock: apricotParentBlk, + onAcceptState: onParentAccept, + } + env.blkManager.(*manager).lastAccepted = parentID + chainTime := env.clk.Time().Truncate(time.Second) + env.mockedState.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() + env.mockedState.EXPECT().GetLastAccepted().Return(parentID).AnyTimes() + + // create a proposal transaction to be included into proposal block + utx := &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{}, + Validator: validator.Validator{End: uint64(chainTime.Unix())}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: env.ctx.AVAXAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationShares: uint32(defaultTxFee), + } + addValTx := &txs.Tx{Unsigned: utx} + require.NoError(addValTx.Sign(txs.Codec, nil)) + blkTx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: addValTx.ID(), + }, + } + + // setup state to validate proposal block transaction + onParentAccept.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() + + currentStakersIt := state.NewMockStakerIterator(ctrl) + currentStakersIt.EXPECT().Next().Return(true) + currentStakersIt.EXPECT().Value().Return(&state.Staker{ + TxID: addValTx.ID(), + NodeID: utx.NodeID(), + SubnetID: utx.SubnetID(), + StartTime: utx.StartTime(), + EndTime: chainTime, + }).Times(2) + currentStakersIt.EXPECT().Release() + onParentAccept.EXPECT().GetCurrentStakerIterator().Return(currentStakersIt, nil) + onParentAccept.EXPECT().GetCurrentValidator(utx.SubnetID(), utx.NodeID()).Return(&state.Staker{ + TxID: addValTx.ID(), + NodeID: utx.NodeID(), + SubnetID: utx.SubnetID(), + StartTime: utx.StartTime(), + EndTime: chainTime, + }, nil) + onParentAccept.EXPECT().GetTx(addValTx.ID()).Return(addValTx, status.Committed, nil) + onParentAccept.EXPECT().GetCurrentSupply(constants.PrimaryNetworkID).Return(uint64(1000), nil).AnyTimes() + + env.mockedState.EXPECT().GetUptime(gomock.Any()).Return( + time.Duration(1000), /*upDuration*/ + time.Time{}, /*lastUpdated*/ + nil, /*err*/ + ).AnyTimes() + + // wrong height + statelessProposalBlock, err := blocks.NewApricotProposalBlock( + parentID, + parentHeight, + blkTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.Error(block.Verify()) + + // valid + statelessProposalBlock, err = blocks.NewApricotProposalBlock( + parentID, + parentHeight+1, + blkTx, + ) + require.NoError(err) + + block = env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(block.Verify()) +} + +func TestBanffProposalBlockTimeVerification(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + env := newEnvironment(t, ctrl) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.clk.Set(defaultGenesisTime) + env.config.BanffTime = time.Time{} // activate Banff + + // create parentBlock. It's a standard one for simplicity + parentTime := defaultGenesisTime + parentHeight := uint64(2022) + + banffParentBlk, err := blocks.NewApricotStandardBlock( + genesisBlkID, // does not matter + parentHeight, + nil, // txs do not matter in this test + ) + require.NoError(err) + parentID := banffParentBlk.ID() + + // store parent block, with relevant quantities + chainTime := parentTime + env.mockedState.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() + + onParentAccept := state.NewMockDiff(ctrl) + onParentAccept.EXPECT().GetTimestamp().Return(parentTime).AnyTimes() + onParentAccept.EXPECT().GetCurrentSupply(constants.PrimaryNetworkID).Return(uint64(1000), nil).AnyTimes() + + env.blkManager.(*manager).blkIDToState[parentID] = &blockState{ + statelessBlock: banffParentBlk, + onAcceptState: onParentAccept, + timestamp: parentTime, + } + env.blkManager.(*manager).lastAccepted = parentID + env.mockedState.EXPECT().GetLastAccepted().Return(parentID).AnyTimes() + env.mockedState.EXPECT().GetStatelessBlock(gomock.Any()).DoAndReturn( + func(blockID ids.ID) (blocks.Block, choices.Status, error) { + if blockID == parentID { + return banffParentBlk, choices.Accepted, nil + } + return nil, choices.Rejected, database.ErrNotFound + }).AnyTimes() + + // setup state to validate proposal block transaction + nextStakerTime := chainTime.Add(executor.SyncBound).Add(-1 * time.Second) + unsignedNextStakerTx := &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{}, + Validator: validator.Validator{End: uint64(nextStakerTime.Unix())}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: env.ctx.AVAXAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationShares: uint32(defaultTxFee), + } + nextStakerTx := &txs.Tx{Unsigned: unsignedNextStakerTx} + require.NoError(nextStakerTx.Sign(txs.Codec, nil)) + + nextStakerTxID := nextStakerTx.ID() + onParentAccept.EXPECT().GetCurrentValidator(unsignedNextStakerTx.SubnetID(), unsignedNextStakerTx.NodeID()).Return(&state.Staker{ + TxID: nextStakerTxID, + NodeID: unsignedNextStakerTx.NodeID(), + SubnetID: unsignedNextStakerTx.SubnetID(), + StartTime: unsignedNextStakerTx.StartTime(), + EndTime: chainTime, + }, nil) + onParentAccept.EXPECT().GetTx(nextStakerTxID).Return(nextStakerTx, status.Processing, nil) + + currentStakersIt := state.NewMockStakerIterator(ctrl) + currentStakersIt.EXPECT().Next().Return(true).AnyTimes() + currentStakersIt.EXPECT().Value().Return(&state.Staker{ + TxID: nextStakerTxID, + EndTime: nextStakerTime, + NextTime: nextStakerTime, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + }).AnyTimes() + currentStakersIt.EXPECT().Release().AnyTimes() + onParentAccept.EXPECT().GetCurrentStakerIterator().Return(currentStakersIt, nil).AnyTimes() + + pendingStakersIt := state.NewMockStakerIterator(ctrl) + pendingStakersIt.EXPECT().Next().Return(false).AnyTimes() // no pending stakers + pendingStakersIt.EXPECT().Release().AnyTimes() + onParentAccept.EXPECT().GetPendingStakerIterator().Return(pendingStakersIt, nil).AnyTimes() + + env.mockedState.EXPECT().GetUptime(gomock.Any()).Return( + time.Duration(1000), /*upDuration*/ + time.Time{}, /*lastUpdated*/ + nil, /*err*/ + ).AnyTimes() + + // create proposal tx to be included in the proposal block + blkTx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: nextStakerTxID, + }, + } + require.NoError(blkTx.Sign(txs.Codec, nil)) + + { + // wrong height + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + parentTime.Add(time.Second), + parentID, + banffParentBlk.Height(), + blkTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.Error(block.Verify()) + } + + { + // wrong version + statelessProposalBlock, err := blocks.NewApricotProposalBlock( + parentID, + banffParentBlk.Height()+1, + blkTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.Error(block.Verify()) + } + + { + // wrong timestamp, earlier than parent + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + parentTime.Add(-1*time.Second), + parentID, + banffParentBlk.Height()+1, + blkTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.Error(block.Verify()) + } + + { + // wrong timestamp, violated synchrony bound + beyondSyncBoundTimeStamp := env.clk.Time().Add(executor.SyncBound).Add(time.Second) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + beyondSyncBoundTimeStamp, + parentID, + banffParentBlk.Height()+1, + blkTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.Error(block.Verify()) + } + + { + // wrong timestamp, skipped staker set change event + skippedStakerEventTimeStamp := nextStakerTime.Add(time.Second) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + skippedStakerEventTimeStamp, + parentID, + banffParentBlk.Height()+1, + blkTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.Error(block.Verify()) + } + + { + // wrong tx content (no advance time txs) + invalidTx := &txs.Tx{ + Unsigned: &txs.AdvanceTimeTx{ + Time: uint64(nextStakerTime.Unix()), + }, + } + require.NoError(invalidTx.Sign(txs.Codec, nil)) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + parentTime.Add(time.Second), + parentID, + banffParentBlk.Height()+1, + invalidTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.Error(block.Verify()) + } + + { + // include too many transactions + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + nextStakerTime, + parentID, + banffParentBlk.Height()+1, + blkTx, + ) + require.NoError(err) + + statelessProposalBlock.Transactions = []*txs.Tx{blkTx} + block := env.blkManager.NewBlock(statelessProposalBlock) + require.ErrorIs(block.Verify(), errBanffProposalBlockWithMultipleTransactions) + } + + { + // valid + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + nextStakerTime, + parentID, + banffParentBlk.Height()+1, + blkTx, + ) + require.NoError(err) + + block := env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(block.Verify()) + } +} + +func TestBanffProposalBlockUpdateStakers(t *testing.T) { + // Chronological order (not in scale): + // Staker0: |--- ??? // Staker0 end time depends on the test + // Staker1: |------------------------------------------------------| + // Staker2: |------------------------| + // Staker3: |------------------------| + // Staker3sub: |----------------| + // Staker4: |------------------------| + // Staker5: |--------------------| + + // Staker0 it's here just to allow to issue a proposal block with the chosen endTime. + staker0RewardAddress := ids.GenerateTestShortID() + staker0 := staker{ + nodeID: ids.NodeID(staker0RewardAddress), + rewardAddress: staker0RewardAddress, + startTime: defaultGenesisTime, + endTime: time.Time{}, // actual endTime depends on specific test + } + + staker1RewardAddress := ids.GenerateTestShortID() + staker1 := staker{ + nodeID: ids.NodeID(staker1RewardAddress), + rewardAddress: staker1RewardAddress, + startTime: defaultGenesisTime.Add(1 * time.Minute), + endTime: defaultGenesisTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute), + } + + staker2RewardAddress := ids.GenerateTestShortID() + staker2 := staker{ + nodeID: ids.NodeID(staker2RewardAddress), + rewardAddress: staker2RewardAddress, + startTime: staker1.startTime.Add(1 * time.Minute), + endTime: staker1.startTime.Add(1 * time.Minute).Add(defaultMinStakingDuration), + } + + staker3RewardAddress := ids.GenerateTestShortID() + staker3 := staker{ + nodeID: ids.NodeID(staker3RewardAddress), + rewardAddress: staker3RewardAddress, + startTime: staker2.startTime.Add(1 * time.Minute), + endTime: staker2.endTime.Add(1 * time.Minute), + } + + staker3Sub := staker{ + nodeID: staker3.nodeID, + rewardAddress: staker3.rewardAddress, + startTime: staker3.startTime.Add(1 * time.Minute), + endTime: staker3.endTime.Add(-1 * time.Minute), + } + + staker4RewardAddress := ids.GenerateTestShortID() + staker4 := staker{ + nodeID: ids.NodeID(staker4RewardAddress), + rewardAddress: staker4RewardAddress, + startTime: staker3.startTime, + endTime: staker3.endTime, + } + + staker5RewardAddress := ids.GenerateTestShortID() + staker5 := staker{ + nodeID: ids.NodeID(staker5RewardAddress), + rewardAddress: staker5RewardAddress, + startTime: staker2.endTime, + endTime: staker2.endTime.Add(defaultMinStakingDuration), + } + + tests := []test{ + { + description: "advance time to before staker1 start with subnet", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + subnetStakers: []staker{staker1, staker2, staker3, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime.Add(-1 * time.Second)}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: pending, + staker2.nodeID: pending, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + expectedSubnetStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: pending, + staker2.nodeID: pending, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + }, + { + description: "advance time to staker 1 start with subnet", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + subnetStakers: []staker{staker1}, + advanceTimeTo: []time.Time{staker1.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: pending, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + expectedSubnetStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: pending, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + }, + { + description: "advance time to the staker2 start", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + }, + { + description: "staker3 should validate only primary network", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + expectedSubnetStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3Sub.nodeID: pending, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + }, + { + description: "advance time to staker3 start with subnet", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker3Sub.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + expectedSubnetStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + }, + { + description: "advance time to staker5 end", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker5.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + + // given its txID, staker2 will be + // rewarded and moved out of current stakers set + // staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: current, + }, + }, + } + + for _, test := range tests { + t.Run(test.description, func(ts *testing.T) { + require := require.New(ts) + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + + env.config.BanffTime = time.Time{} // activate Banff + env.config.WhitelistedSubnets.Add(testSubnet1.ID()) + + for _, staker := range test.stakers { + tx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, + uint64(staker.startTime.Unix()), + uint64(staker.endTime.Unix()), + staker.nodeID, + staker.rewardAddress, + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker := state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddValidatorTx), + ) + + env.state.PutPendingValidator(staker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + } + + for _, subStaker := range test.subnetStakers { + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + 10, // Weight + uint64(subStaker.startTime.Unix()), + uint64(subStaker.endTime.Unix()), + subStaker.nodeID, // validator ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + subnetStaker := state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) + + env.state.PutPendingValidator(subnetStaker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + } + + for _, newTime := range test.advanceTimeTo { + env.clk.Set(newTime) + + // add Staker0 (with the right end time) to state + // so to allow proposalBlk issuance + staker0.endTime = newTime + addStaker0, err := env.txBuilder.NewAddValidatorTx( + 10, + uint64(staker0.startTime.Unix()), + uint64(staker0.endTime.Unix()), + staker0.nodeID, + staker0.rewardAddress, + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + // store Staker0 to state + staker0 := state.NewCurrentStaker( + addStaker0.ID(), + addStaker0.Unsigned.(*txs.AddValidatorTx), + 0, + ) + env.state.PutCurrentValidator(staker0) + env.state.AddTx(addStaker0, status.Committed) + require.NoError(env.state.Commit()) + + s0RewardTx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: staker0.TxID, + }, + } + require.NoError(s0RewardTx.Sign(txs.Codec, nil)) + + // build proposal block moving ahead chain time + // as well as rewarding staker0 + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + newTime, + parentBlk.ID(), + parentBlk.Height()+1, + s0RewardTx, + ) + require.NoError(err) + + // verify and accept the block + block := env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(block.Verify()) + options, err := block.(snowman.OracleBlock).Options() + require.NoError(err) + + require.NoError(options[0].Verify()) + + require.NoError(block.Accept()) + require.NoError(options[0].Accept()) + } + require.NoError(env.state.Commit()) + + for stakerNodeID, status := range test.expectedStakers { + switch status { + case pending: + _, err := env.state.GetPendingValidator(constants.PrimaryNetworkID, stakerNodeID) + require.NoError(err) + require.False(env.config.Validators.Contains(constants.PrimaryNetworkID, stakerNodeID)) + case current: + _, err := env.state.GetCurrentValidator(constants.PrimaryNetworkID, stakerNodeID) + require.NoError(err) + require.True(env.config.Validators.Contains(constants.PrimaryNetworkID, stakerNodeID)) + } + } + + for stakerNodeID, status := range test.expectedSubnetStakers { + switch status { + case pending: + require.False(env.config.Validators.Contains(testSubnet1.ID(), stakerNodeID)) + case current: + require.True(env.config.Validators.Contains(testSubnet1.ID(), stakerNodeID)) + } + } + }) + } +} + +func TestBanffProposalBlockRemoveSubnetValidator(t *testing.T) { + require := require.New(t) + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + env.config.WhitelistedSubnets.Add(testSubnet1.ID()) + + // Add a subnet validator to the staker set + subnetValidatorNodeID := ids.NodeID(preFundedKeys[0].PublicKey().Address()) + // Starts after the corre + subnetVdr1StartTime := defaultValidateStartTime + subnetVdr1EndTime := defaultValidateStartTime.Add(defaultMinStakingDuration) + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetVdr1StartTime.Unix()), // Start time + uint64(subnetVdr1EndTime.Unix()), // end time + subnetValidatorNodeID, // Node ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + 0, + ) + + env.state.PutCurrentValidator(staker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + + // The above validator is now part of the staking set + + // Queue a staker that joins the staker set after the above validator leaves + subnetVdr2NodeID := ids.NodeID(preFundedKeys[1].PublicKey().Address()) + tx, err = env.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetVdr1EndTime.Add(time.Second).Unix()), // Start time + uint64(subnetVdr1EndTime.Add(time.Second).Add(defaultMinStakingDuration).Unix()), // end time + subnetVdr2NodeID, // Node ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker = state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) + + env.state.PutPendingValidator(staker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + + // The above validator is now in the pending staker set + + // Advance time to the first staker's end time. + env.clk.Set(subnetVdr1EndTime) + + // add Staker0 (with the right end time) to state + // so to allow proposalBlk issuance + staker0StartTime := defaultValidateStartTime + staker0EndTime := subnetVdr1EndTime + addStaker0, err := env.txBuilder.NewAddValidatorTx( + 10, + uint64(staker0StartTime.Unix()), + uint64(staker0EndTime.Unix()), + ids.GenerateTestNodeID(), + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + // store Staker0 to state + staker = state.NewCurrentStaker( + addStaker0.ID(), + addStaker0.Unsigned.(*txs.AddValidatorTx), + 0, + ) + env.state.PutCurrentValidator(staker) + env.state.AddTx(addStaker0, status.Committed) + require.NoError(env.state.Commit()) + + // create rewardTx for staker0 + s0RewardTx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: addStaker0.ID(), + }, + } + require.NoError(s0RewardTx.Sign(txs.Codec, nil)) + + // build proposal block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + subnetVdr1EndTime, + parentBlk.ID(), + parentBlk.Height()+1, + s0RewardTx, + ) + require.NoError(err) + propBlk := env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(propBlk.Verify()) // verify and update staker set + + options, err := propBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk := options[0] + require.NoError(commitBlk.Verify()) + + blkStateMap := env.blkManager.(*manager).blkIDToState + updatedState := blkStateMap[commitBlk.ID()].onAcceptState + _, err = updatedState.GetCurrentValidator(testSubnet1.ID(), subnetValidatorNodeID) + require.ErrorIs(err, database.ErrNotFound) + + // Check VM Validators are removed successfully + require.NoError(propBlk.Accept()) + require.NoError(commitBlk.Accept()) + require.False(env.config.Validators.Contains(testSubnet1.ID(), subnetVdr2NodeID)) + require.False(env.config.Validators.Contains(testSubnet1.ID(), subnetValidatorNodeID)) +} + +func TestBanffProposalBlockWhitelistedSubnet(t *testing.T) { + require := require.New(t) + + for _, whitelist := range []bool{true, false} { + t.Run(fmt.Sprintf("whitelisted %t", whitelist), func(ts *testing.T) { + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + if whitelist { + env.config.WhitelistedSubnets.Add(testSubnet1.ID()) + } + + // Add a subnet validator to the staker set + subnetValidatorNodeID := ids.NodeID(preFundedKeys[0].PublicKey().Address()) + + subnetVdr1StartTime := defaultGenesisTime.Add(1 * time.Minute) + subnetVdr1EndTime := defaultGenesisTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute) + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetVdr1StartTime.Unix()), // Start time + uint64(subnetVdr1EndTime.Unix()), // end time + subnetValidatorNodeID, // Node ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker := state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) + + env.state.PutPendingValidator(staker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + + // Advance time to the staker's start time. + env.clk.Set(subnetVdr1StartTime) + + // add Staker0 (with the right end time) to state + // so to allow proposalBlk issuance + staker0StartTime := defaultGenesisTime + staker0EndTime := subnetVdr1StartTime + addStaker0, err := env.txBuilder.NewAddValidatorTx( + 10, + uint64(staker0StartTime.Unix()), + uint64(staker0EndTime.Unix()), + ids.GenerateTestNodeID(), + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + // store Staker0 to state + staker = state.NewCurrentStaker( + addStaker0.ID(), + addStaker0.Unsigned.(*txs.AddValidatorTx), + 0, + ) + env.state.PutCurrentValidator(staker) + env.state.AddTx(addStaker0, status.Committed) + require.NoError(env.state.Commit()) + + // create rewardTx for staker0 + s0RewardTx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: addStaker0.ID(), + }, + } + require.NoError(s0RewardTx.Sign(txs.Codec, nil)) + + // build proposal block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + subnetVdr1StartTime, + parentBlk.ID(), + parentBlk.Height()+1, + s0RewardTx, + ) + require.NoError(err) + propBlk := env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(propBlk.Verify()) // verify update staker set + options, err := propBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk := options[0] + require.NoError(commitBlk.Verify()) + + require.NoError(propBlk.Accept()) + require.NoError(commitBlk.Accept()) + require.Equal(whitelist, env.config.Validators.Contains(testSubnet1.ID(), subnetValidatorNodeID)) + }) + } +} + +func TestBanffProposalBlockDelegatorStakerWeight(t *testing.T) { + require := require.New(t) + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + + // Case: Timestamp is after next validator start time + // Add a pending validator + pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) + pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMaxStakingDuration) + nodeID := ids.GenerateTestNodeID() + rewardAddress := ids.GenerateTestShortID() + _, err := addPendingValidator( + env, + pendingValidatorStartTime, + pendingValidatorEndTime, + nodeID, + rewardAddress, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ) + require.NoError(err) + + // add Staker0 (with the right end time) to state + // just to allow proposalBlk issuance (with a reward Tx) + staker0StartTime := defaultGenesisTime + staker0EndTime := pendingValidatorStartTime + addStaker0, err := env.txBuilder.NewAddValidatorTx( + 10, + uint64(staker0StartTime.Unix()), + uint64(staker0EndTime.Unix()), + ids.GenerateTestNodeID(), + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + // store Staker0 to state + staker := state.NewCurrentStaker( + addStaker0.ID(), + addStaker0.Unsigned.(*txs.AddValidatorTx), + 0, + ) + env.state.PutCurrentValidator(staker) + env.state.AddTx(addStaker0, status.Committed) + require.NoError(env.state.Commit()) + + // create rewardTx for staker0 + s0RewardTx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: addStaker0.ID(), + }, + } + require.NoError(s0RewardTx.Sign(txs.Codec, nil)) + + // build proposal block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + pendingValidatorStartTime, + parentBlk.ID(), + parentBlk.Height()+1, + s0RewardTx, + ) + require.NoError(err) + propBlk := env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(propBlk.Verify()) + + options, err := propBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk := options[0] + require.NoError(commitBlk.Verify()) + + require.NoError(propBlk.Accept()) + require.NoError(commitBlk.Accept()) + + // Test validator weight before delegation + primarySet, ok := env.config.Validators.GetValidators(constants.PrimaryNetworkID) + require.True(ok) + vdrWeight, _ := primarySet.GetWeight(nodeID) + require.Equal(env.config.MinValidatorStake, vdrWeight) + + // Add delegator + pendingDelegatorStartTime := pendingValidatorStartTime.Add(1 * time.Second) + pendingDelegatorEndTime := pendingDelegatorStartTime.Add(1 * time.Second) + + addDelegatorTx, err := env.txBuilder.NewAddDelegatorTx( + env.config.MinDelegatorStake, + uint64(pendingDelegatorStartTime.Unix()), + uint64(pendingDelegatorEndTime.Unix()), + nodeID, + preFundedKeys[0].PublicKey().Address(), + []*crypto.PrivateKeySECP256K1R{ + preFundedKeys[0], + preFundedKeys[1], + preFundedKeys[4], + }, + ids.ShortEmpty, + ) + require.NoError(err) + + staker = state.NewPendingStaker( + addDelegatorTx.ID(), + addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), + ) + + env.state.PutPendingDelegator(staker) + env.state.AddTx(addDelegatorTx, status.Committed) + env.state.SetHeight( /*dummyHeight*/ uint64(1)) + require.NoError(env.state.Commit()) + + // add Staker0 (with the right end time) to state + // so to allow proposalBlk issuance + staker0EndTime = pendingDelegatorStartTime + addStaker0, err = env.txBuilder.NewAddValidatorTx( + 10, + uint64(staker0StartTime.Unix()), + uint64(staker0EndTime.Unix()), + ids.GenerateTestNodeID(), + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + // store Staker0 to state + staker = state.NewCurrentStaker( + addStaker0.ID(), + addStaker0.Unsigned.(*txs.AddValidatorTx), + 0, + ) + env.state.PutCurrentValidator(staker) + env.state.AddTx(addStaker0, status.Committed) + require.NoError(env.state.Commit()) + + // create rewardTx for staker0 + s0RewardTx = &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: addStaker0.ID(), + }, + } + require.NoError(s0RewardTx.Sign(txs.Codec, nil)) + + // Advance Time + preferredID = env.state.GetLastAccepted() + parentBlk, _, err = env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessProposalBlock, err = blocks.NewBanffProposalBlock( + pendingDelegatorStartTime, + parentBlk.ID(), + parentBlk.Height()+1, + s0RewardTx, + ) + require.NoError(err) + + propBlk = env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(propBlk.Verify()) + + options, err = propBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk = options[0] + require.NoError(commitBlk.Verify()) + + require.NoError(propBlk.Accept()) + require.NoError(commitBlk.Accept()) + + // Test validator weight after delegation + vdrWeight, _ = primarySet.GetWeight(nodeID) + require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight) +} + +func TestBanffProposalBlockDelegatorStakers(t *testing.T) { + require := require.New(t) + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + + // Case: Timestamp is after next validator start time + // Add a pending validator + pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) + pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) + factory := crypto.FactorySECP256K1R{} + nodeIDKey, _ := factory.NewPrivateKey() + rewardAddress := nodeIDKey.PublicKey().Address() + nodeID := ids.NodeID(rewardAddress) + + _, err := addPendingValidator( + env, + pendingValidatorStartTime, + pendingValidatorEndTime, + nodeID, + rewardAddress, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ) + require.NoError(err) + + // add Staker0 (with the right end time) to state + // so to allow proposalBlk issuance + staker0StartTime := defaultGenesisTime + staker0EndTime := pendingValidatorStartTime + addStaker0, err := env.txBuilder.NewAddValidatorTx( + 10, + uint64(staker0StartTime.Unix()), + uint64(staker0EndTime.Unix()), + ids.GenerateTestNodeID(), + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + // store Staker0 to state + staker := state.NewCurrentStaker( + addStaker0.ID(), + addStaker0.Unsigned.(*txs.AddValidatorTx), + 0, + ) + env.state.PutCurrentValidator(staker) + env.state.AddTx(addStaker0, status.Committed) + require.NoError(env.state.Commit()) + + // create rewardTx for staker0 + s0RewardTx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: addStaker0.ID(), + }, + } + require.NoError(s0RewardTx.Sign(txs.Codec, nil)) + + // build proposal block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessProposalBlock, err := blocks.NewBanffProposalBlock( + pendingValidatorStartTime, + parentBlk.ID(), + parentBlk.Height()+1, + s0RewardTx, + ) + require.NoError(err) + propBlk := env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(propBlk.Verify()) + + options, err := propBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk := options[0] + require.NoError(commitBlk.Verify()) + + require.NoError(propBlk.Accept()) + require.NoError(commitBlk.Accept()) + + // Test validator weight before delegation + primarySet, ok := env.config.Validators.GetValidators(constants.PrimaryNetworkID) + require.True(ok) + vdrWeight, _ := primarySet.GetWeight(nodeID) + require.Equal(env.config.MinValidatorStake, vdrWeight) + + // Add delegator + pendingDelegatorStartTime := pendingValidatorStartTime.Add(1 * time.Second) + pendingDelegatorEndTime := pendingDelegatorStartTime.Add(defaultMinStakingDuration) + addDelegatorTx, err := env.txBuilder.NewAddDelegatorTx( + env.config.MinDelegatorStake, + uint64(pendingDelegatorStartTime.Unix()), + uint64(pendingDelegatorEndTime.Unix()), + nodeID, + preFundedKeys[0].PublicKey().Address(), + []*crypto.PrivateKeySECP256K1R{ + preFundedKeys[0], + preFundedKeys[1], + preFundedKeys[4], + }, + ids.ShortEmpty, + ) + require.NoError(err) + + staker = state.NewPendingStaker( + addDelegatorTx.ID(), + addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), + ) + + env.state.PutPendingDelegator(staker) + env.state.AddTx(addDelegatorTx, status.Committed) + env.state.SetHeight( /*dummyHeight*/ uint64(1)) + require.NoError(env.state.Commit()) + + // add Staker0 (with the right end time) to state + // so to allow proposalBlk issuance + staker0EndTime = pendingDelegatorStartTime + addStaker0, err = env.txBuilder.NewAddValidatorTx( + 10, + uint64(staker0StartTime.Unix()), + uint64(staker0EndTime.Unix()), + ids.GenerateTestNodeID(), + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + // store Staker0 to state + staker = state.NewCurrentStaker( + addStaker0.ID(), + addStaker0.Unsigned.(*txs.AddValidatorTx), + 0, + ) + env.state.PutCurrentValidator(staker) + env.state.AddTx(addStaker0, status.Committed) + require.NoError(env.state.Commit()) + + // create rewardTx for staker0 + s0RewardTx = &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: addStaker0.ID(), + }, + } + require.NoError(s0RewardTx.Sign(txs.Codec, nil)) + + // Advance Time + preferredID = env.state.GetLastAccepted() + parentBlk, _, err = env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessProposalBlock, err = blocks.NewBanffProposalBlock( + pendingDelegatorStartTime, + parentBlk.ID(), + parentBlk.Height()+1, + s0RewardTx, + ) + require.NoError(err) + propBlk = env.blkManager.NewBlock(statelessProposalBlock) + require.NoError(propBlk.Verify()) + + options, err = propBlk.(snowman.OracleBlock).Options() + require.NoError(err) + commitBlk = options[0] + require.NoError(commitBlk.Verify()) + + require.NoError(propBlk.Accept()) + require.NoError(commitBlk.Accept()) + + // Test validator weight after delegation + vdrWeight, _ = primarySet.GetWeight(nodeID) + require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight) +} diff --git a/avalanchego/vms/platformvm/blocks/executor/rejector.go b/avalanchego/vms/platformvm/blocks/executor/rejector.go index f83ca875..5fbdac2c 100644 --- a/avalanchego/vms/platformvm/blocks/executor/rejector.go +++ b/avalanchego/vms/platformvm/blocks/executor/rejector.go @@ -19,28 +19,46 @@ type rejector struct { *backend } -func (r *rejector) ProposalBlock(b *blocks.ProposalBlock) error { - return r.rejectBlock(b, "proposal") +func (r *rejector) BanffAbortBlock(b *blocks.BanffAbortBlock) error { + return r.rejectBlock(b, "banff abort") } -func (r *rejector) AtomicBlock(b *blocks.AtomicBlock) error { - return r.rejectBlock(b, "atomic") +func (r *rejector) BanffCommitBlock(b *blocks.BanffCommitBlock) error { + return r.rejectBlock(b, "banff commit") } -func (r *rejector) StandardBlock(b *blocks.StandardBlock) error { - return r.rejectBlock(b, "standard") +func (r *rejector) BanffProposalBlock(b *blocks.BanffProposalBlock) error { + return r.rejectBlock(b, "banff proposal") } -func (r *rejector) CommitBlock(b *blocks.CommitBlock) error { - return r.rejectBlock(b, "commit") +func (r *rejector) BanffStandardBlock(b *blocks.BanffStandardBlock) error { + return r.rejectBlock(b, "banff standard") } -func (r *rejector) AbortBlock(b *blocks.AbortBlock) error { - return r.rejectBlock(b, "abort") +func (r *rejector) ApricotAbortBlock(b *blocks.ApricotAbortBlock) error { + return r.rejectBlock(b, "apricot abort") +} + +func (r *rejector) ApricotCommitBlock(b *blocks.ApricotCommitBlock) error { + return r.rejectBlock(b, "apricot commit") +} + +func (r *rejector) ApricotProposalBlock(b *blocks.ApricotProposalBlock) error { + return r.rejectBlock(b, "apricot proposal") +} + +func (r *rejector) ApricotStandardBlock(b *blocks.ApricotStandardBlock) error { + return r.rejectBlock(b, "apricot standard") +} + +func (r *rejector) ApricotAtomicBlock(b *blocks.ApricotAtomicBlock) error { + return r.rejectBlock(b, "apricot atomic") } func (r *rejector) rejectBlock(b blocks.Block, blockType string) error { blkID := b.ID() + defer r.free(blkID) + r.ctx.Log.Verbo( "rejecting block", zap.String("blockType", blockType), @@ -61,7 +79,5 @@ func (r *rejector) rejectBlock(b blocks.Block, blockType string) error { } r.state.AddStatelessBlock(b, choices.Rejected) - err := r.state.Commit() - r.free(blkID) - return err + return r.state.Commit() } diff --git a/avalanchego/vms/platformvm/blocks/executor/rejector_test.go b/avalanchego/vms/platformvm/blocks/executor/rejector_test.go index 0c684443..48b51ed4 100644 --- a/avalanchego/vms/platformvm/blocks/executor/rejector_test.go +++ b/avalanchego/vms/platformvm/blocks/executor/rejector_test.go @@ -33,52 +33,52 @@ func TestRejectBlock(t *testing.T) { { name: "proposal block", newBlockFunc: func() (blocks.Block, error) { - return blocks.NewProposalBlock( + return blocks.NewApricotProposalBlock( ids.GenerateTestID(), 1, &txs.Tx{ Unsigned: &txs.AddDelegatorTx{ // Without the line below, this function will error. - RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{}, }, Creds: []verify.Verifiable{}, }, ) }, rejectFunc: func(r *rejector, b blocks.Block) error { - return r.ProposalBlock(b.(*blocks.ProposalBlock)) + return r.ApricotProposalBlock(b.(*blocks.ApricotProposalBlock)) }, }, { name: "atomic block", newBlockFunc: func() (blocks.Block, error) { - return blocks.NewAtomicBlock( + return blocks.NewApricotAtomicBlock( ids.GenerateTestID(), 1, &txs.Tx{ Unsigned: &txs.AddDelegatorTx{ // Without the line below, this function will error. - RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{}, }, Creds: []verify.Verifiable{}, }, ) }, rejectFunc: func(r *rejector, b blocks.Block) error { - return r.AtomicBlock(b.(*blocks.AtomicBlock)) + return r.ApricotAtomicBlock(b.(*blocks.ApricotAtomicBlock)) }, }, { name: "standard block", newBlockFunc: func() (blocks.Block, error) { - return blocks.NewStandardBlock( + return blocks.NewApricotStandardBlock( ids.GenerateTestID(), 1, []*txs.Tx{ { Unsigned: &txs.AddDelegatorTx{ // Without the line below, this function will error. - RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{}, }, Creds: []verify.Verifiable{}, }, @@ -86,31 +86,25 @@ func TestRejectBlock(t *testing.T) { ) }, rejectFunc: func(r *rejector, b blocks.Block) error { - return r.StandardBlock(b.(*blocks.StandardBlock)) + return r.ApricotStandardBlock(b.(*blocks.ApricotStandardBlock)) }, }, { name: "commit", newBlockFunc: func() (blocks.Block, error) { - return blocks.NewCommitBlock( - ids.GenerateTestID(), - 1, - ) + return blocks.NewApricotCommitBlock(ids.GenerateTestID() /*parent*/, 1 /*height*/) }, rejectFunc: func(r *rejector, blk blocks.Block) error { - return r.CommitBlock(blk.(*blocks.CommitBlock)) + return r.ApricotCommitBlock(blk.(*blocks.ApricotCommitBlock)) }, }, { name: "abort", newBlockFunc: func() (blocks.Block, error) { - return blocks.NewAbortBlock( - ids.GenerateTestID(), - 1, - ) + return blocks.NewApricotAbortBlock(ids.GenerateTestID() /*parent*/, 1 /*height*/) }, rejectFunc: func(r *rejector, blk blocks.Block) error { - return r.AbortBlock(blk.(*blocks.AbortBlock)) + return r.ApricotAbortBlock(blk.(*blocks.ApricotAbortBlock)) }, }, } diff --git a/avalanchego/vms/platformvm/blocks/executor/standard_block_test.go b/avalanchego/vms/platformvm/blocks/executor/standard_block_test.go new file mode 100644 index 00000000..31143a9c --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/executor/standard_block_test.go @@ -0,0 +1,837 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "fmt" + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/status" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + txexecutor "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" +) + +func TestApricotStandardBlockTimeVerification(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + env := newEnvironment(t, ctrl) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + + // setup and store parent block + // it's a standard block for simplicity + parentHeight := uint64(2022) + + apricotParentBlk, err := blocks.NewApricotStandardBlock( + ids.Empty, // does not matter + parentHeight, + nil, // txs do not matter in this test + ) + require.NoError(err) + parentID := apricotParentBlk.ID() + + // store parent block, with relevant quantities + onParentAccept := state.NewMockDiff(ctrl) + env.blkManager.(*manager).blkIDToState[parentID] = &blockState{ + statelessBlock: apricotParentBlk, + onAcceptState: onParentAccept, + } + env.blkManager.(*manager).lastAccepted = parentID + + chainTime := env.clk.Time().Truncate(time.Second) + env.mockedState.EXPECT().GetLastAccepted().Return(parentID).AnyTimes() + env.mockedState.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() + onParentAccept.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() + + // wrong height + apricotChildBlk, err := blocks.NewApricotStandardBlock( + apricotParentBlk.ID(), + apricotParentBlk.Height(), + nil, // txs nulled to simplify test + ) + require.NoError(err) + block := env.blkManager.NewBlock(apricotChildBlk) + require.Error(block.Verify()) + + // valid height + apricotChildBlk, err = blocks.NewApricotStandardBlock( + apricotParentBlk.ID(), + apricotParentBlk.Height()+1, + nil, // txs nulled to simplify test + ) + require.NoError(err) + block = env.blkManager.NewBlock(apricotChildBlk) + require.NoError(block.Verify()) +} + +func TestBanffStandardBlockTimeVerification(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + env := newEnvironment(t, ctrl) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + now := env.clk.Time() + env.clk.Set(now) + env.config.BanffTime = time.Time{} // activate Banff + + // setup and store parent block + // it's a standard block for simplicity + parentTime := now + parentHeight := uint64(2022) + + banffParentBlk, err := blocks.NewBanffStandardBlock( + parentTime, + ids.Empty, // does not matter + parentHeight, + nil, // txs do not matter in this test + ) + require.NoError(err) + parentID := banffParentBlk.ID() + + // store parent block, with relevant quantities + onParentAccept := state.NewMockDiff(ctrl) + chainTime := env.clk.Time().Truncate(time.Second) + env.blkManager.(*manager).blkIDToState[parentID] = &blockState{ + statelessBlock: banffParentBlk, + onAcceptState: onParentAccept, + timestamp: chainTime, + } + env.blkManager.(*manager).lastAccepted = parentID + env.mockedState.EXPECT().GetLastAccepted().Return(parentID).AnyTimes() + env.mockedState.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() + + nextStakerTime := chainTime.Add(txexecutor.SyncBound).Add(-1 * time.Second) + + // store just once current staker to mark next staker time. + currentStakerIt := state.NewMockStakerIterator(ctrl) + currentStakerIt.EXPECT().Next().Return(true).AnyTimes() + currentStakerIt.EXPECT().Value().Return( + &state.Staker{ + NextTime: nextStakerTime, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + }, + ).AnyTimes() + currentStakerIt.EXPECT().Release().Return().AnyTimes() + onParentAccept.EXPECT().GetCurrentStakerIterator().Return(currentStakerIt, nil).AnyTimes() + + // no pending stakers + pendingIt := state.NewMockStakerIterator(ctrl) + pendingIt.EXPECT().Next().Return(false).AnyTimes() + pendingIt.EXPECT().Release().Return().AnyTimes() + onParentAccept.EXPECT().GetPendingStakerIterator().Return(pendingIt, nil).AnyTimes() + + onParentAccept.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() + + txID := ids.GenerateTestID() + utxo := &avax.UTXO{ + UTXOID: avax.UTXOID{ + TxID: txID, + }, + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: env.config.CreateSubnetTxFee, + }, + } + utxoID := utxo.InputID() + onParentAccept.EXPECT().GetUTXO(utxoID).Return(utxo, nil).AnyTimes() + + // Create the tx + utx := &txs.CreateSubnetTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: env.ctx.NetworkID, + BlockchainID: env.ctx.ChainID, + Ins: []*avax.TransferableInput{{ + UTXOID: utxo.UTXOID, + Asset: utxo.Asset, + In: &secp256k1fx.TransferInput{ + Amt: env.config.CreateSubnetTxFee, + }, + }}, + }}, + Owner: &secp256k1fx.OutputOwners{}, + } + tx := &txs.Tx{Unsigned: utx} + require.NoError(tx.Sign(txs.Codec, [][]*crypto.PrivateKeySECP256K1R{{}})) + + { + // wrong version + banffChildBlk, err := blocks.NewApricotStandardBlock( + banffParentBlk.ID(), + banffParentBlk.Height()+1, + []*txs.Tx{tx}, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.Error(block.Verify()) + } + + { + // wrong height + childTimestamp := parentTime.Add(time.Second) + banffChildBlk, err := blocks.NewBanffStandardBlock( + childTimestamp, + banffParentBlk.ID(), + banffParentBlk.Height(), + []*txs.Tx{tx}, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.Error(block.Verify()) + } + + { + // wrong timestamp, earlier than parent + childTimestamp := parentTime.Add(-1 * time.Second) + banffChildBlk, err := blocks.NewBanffStandardBlock( + childTimestamp, + banffParentBlk.ID(), + banffParentBlk.Height()+1, + []*txs.Tx{tx}, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.Error(block.Verify()) + } + + { + // wrong timestamp, violated synchrony bound + childTimestamp := parentTime.Add(txexecutor.SyncBound).Add(time.Second) + banffChildBlk, err := blocks.NewBanffStandardBlock( + childTimestamp, + banffParentBlk.ID(), + banffParentBlk.Height()+1, + []*txs.Tx{tx}, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.Error(block.Verify()) + } + + { + // wrong timestamp, skipped staker set change event + childTimestamp := nextStakerTime.Add(time.Second) + banffChildBlk, err := blocks.NewBanffStandardBlock( + childTimestamp, + banffParentBlk.ID(), + banffParentBlk.Height()+1, + []*txs.Tx{tx}, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.Error(block.Verify()) + } + + { + // no state changes + childTimestamp := parentTime + banffChildBlk, err := blocks.NewBanffStandardBlock( + childTimestamp, + banffParentBlk.ID(), + banffParentBlk.Height()+1, + nil, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.ErrorIs(block.Verify(), errBanffStandardBlockWithoutChanges) + } + + { + // valid block, same timestamp as parent block + childTimestamp := parentTime + banffChildBlk, err := blocks.NewBanffStandardBlock( + childTimestamp, + banffParentBlk.ID(), + banffParentBlk.Height()+1, + []*txs.Tx{tx}, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.NoError(block.Verify()) + } + + { + // valid + childTimestamp := nextStakerTime + banffChildBlk, err := blocks.NewBanffStandardBlock( + childTimestamp, + banffParentBlk.ID(), + banffParentBlk.Height()+1, + []*txs.Tx{tx}, + ) + require.NoError(err) + block := env.blkManager.NewBlock(banffChildBlk) + require.NoError(block.Verify()) + } +} + +func TestBanffStandardBlockUpdatePrimaryNetworkStakers(t *testing.T) { + require := require.New(t) + + env := newEnvironment(t, nil) + defer func() { + require.NoError(shutdownEnvironment(env)) + }() + env.config.BanffTime = time.Time{} // activate Banff + + // Case: Timestamp is after next validator start time + // Add a pending validator + pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) + pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) + nodeID := ids.GenerateTestNodeID() + rewardAddress := ids.GenerateTestShortID() + addPendingValidatorTx, err := addPendingValidator( + env, + pendingValidatorStartTime, + pendingValidatorEndTime, + nodeID, + rewardAddress, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ) + require.NoError(err) + + // build standard block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessStandardBlock, err := blocks.NewBanffStandardBlock( + pendingValidatorStartTime, + parentBlk.ID(), + parentBlk.Height()+1, + nil, // txs nulled to simplify test + ) + require.NoError(err) + block := env.blkManager.NewBlock(statelessStandardBlock) + + // update staker set + require.NoError(block.Verify()) + + // tests + blkStateMap := env.blkManager.(*manager).blkIDToState + updatedState := blkStateMap[block.ID()].onAcceptState + currentValidator, err := updatedState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + require.True(currentValidator.TxID == addPendingValidatorTx.ID(), "Added the wrong tx to the validator set") + require.EqualValues(0, currentValidator.PotentialReward) // Rewards are 0 on Flare + + _, err = updatedState.GetPendingValidator(constants.PrimaryNetworkID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + // Test VM validators + require.NoError(block.Accept()) + require.True(env.config.Validators.Contains(constants.PrimaryNetworkID, nodeID)) +} + +// Ensure semantic verification updates the current and pending staker sets correctly. +// Namely, it should add pending stakers whose start time is at or before the timestamp. +// It will not remove primary network stakers; that happens in rewardTxs. +func TestBanffStandardBlockUpdateStakers(t *testing.T) { + // Chronological order (not in scale): + // Staker1: |----------------------------------------------------------| + // Staker2: |------------------------| + // Staker3: |------------------------| + // Staker3sub: |----------------| + // Staker4: |------------------------| + // Staker5: |--------------------| + staker1 := staker{ + nodeID: ids.GenerateTestNodeID(), + rewardAddress: ids.GenerateTestShortID(), + startTime: defaultGenesisTime.Add(1 * time.Minute), + endTime: defaultGenesisTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute), + } + staker2 := staker{ + nodeID: ids.GenerateTestNodeID(), + rewardAddress: ids.GenerateTestShortID(), + startTime: staker1.startTime.Add(1 * time.Minute), + endTime: staker1.startTime.Add(1 * time.Minute).Add(defaultMinStakingDuration), + } + staker3 := staker{ + nodeID: ids.GenerateTestNodeID(), + rewardAddress: ids.GenerateTestShortID(), + startTime: staker2.startTime.Add(1 * time.Minute), + endTime: staker2.endTime.Add(1 * time.Minute), + } + staker3Sub := staker{ + nodeID: staker3.nodeID, + rewardAddress: ids.GenerateTestShortID(), + startTime: staker3.startTime.Add(1 * time.Minute), + endTime: staker3.endTime.Add(-1 * time.Minute), + } + staker4 := staker{ + nodeID: ids.GenerateTestNodeID(), + rewardAddress: ids.GenerateTestShortID(), + startTime: staker3.startTime, + endTime: staker3.endTime, + } + staker5 := staker{ + nodeID: ids.GenerateTestNodeID(), + rewardAddress: ids.GenerateTestShortID(), + startTime: staker2.endTime, + endTime: staker2.endTime.Add(defaultMinStakingDuration), + } + + tests := []test{ + { + description: "advance time to staker 1 start with subnet", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + subnetStakers: []staker{staker1}, + advanceTimeTo: []time.Time{staker1.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: pending, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + expectedSubnetStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: pending, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + }, + { + description: "advance time to the staker2 start", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: pending, + staker4.nodeID: pending, + staker5.nodeID: pending, + }, + }, + { + description: "staker3 should validate only primary network", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + expectedSubnetStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3Sub.nodeID: pending, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + }, + { + description: "advance time to staker3 start with subnet", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker3Sub.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + expectedSubnetStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: pending, + }, + }, + { + description: "advance time to staker5 end", + stakers: []staker{staker1, staker2, staker3, staker4, staker5}, + advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker5.startTime}, + expectedStakers: map[ids.NodeID]stakerStatus{ + staker1.nodeID: current, + staker2.nodeID: current, + staker3.nodeID: current, + staker4.nodeID: current, + staker5.nodeID: current, + }, + }, + } + + for _, test := range tests { + t.Run(test.description, func(ts *testing.T) { + require := require.New(ts) + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + env.config.WhitelistedSubnets.Add(testSubnet1.ID()) + + for _, staker := range test.stakers { + _, err := addPendingValidator( + env, + staker.startTime, + staker.endTime, + staker.nodeID, + staker.rewardAddress, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ) + require.NoError(err) + } + + for _, staker := range test.subnetStakers { + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + 10, // Weight + uint64(staker.startTime.Unix()), + uint64(staker.endTime.Unix()), + staker.nodeID, // validator ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker := state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) + + env.state.PutPendingValidator(staker) + env.state.AddTx(tx, status.Committed) + } + env.state.SetHeight( /*dummyHeight*/ 1) + require.NoError(env.state.Commit()) + + for _, newTime := range test.advanceTimeTo { + env.clk.Set(newTime) + + // build standard block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessStandardBlock, err := blocks.NewBanffStandardBlock( + newTime, + parentBlk.ID(), + parentBlk.Height()+1, + nil, // txs nulled to simplify test + ) + block := env.blkManager.NewBlock(statelessStandardBlock) + + require.NoError(err) + + // update staker set + require.NoError(block.Verify()) + require.NoError(block.Accept()) + } + + for stakerNodeID, status := range test.expectedStakers { + switch status { + case pending: + _, err := env.state.GetPendingValidator(constants.PrimaryNetworkID, stakerNodeID) + require.NoError(err) + require.False(env.config.Validators.Contains(constants.PrimaryNetworkID, stakerNodeID)) + case current: + _, err := env.state.GetCurrentValidator(constants.PrimaryNetworkID, stakerNodeID) + require.NoError(err) + require.True(env.config.Validators.Contains(constants.PrimaryNetworkID, stakerNodeID)) + } + } + + for stakerNodeID, status := range test.expectedSubnetStakers { + switch status { + case pending: + require.False(env.config.Validators.Contains(testSubnet1.ID(), stakerNodeID)) + case current: + require.True(env.config.Validators.Contains(testSubnet1.ID(), stakerNodeID)) + } + } + }) + } +} + +// Regression test for https://github.com/ava-labs/avalanchego/pull/584 +// that ensures it fixes a bug where subnet validators are not removed +// when timestamp is advanced and there is a pending staker whose start time +// is after the new timestamp +func TestBanffStandardBlockRemoveSubnetValidator(t *testing.T) { + require := require.New(t) + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + env.config.WhitelistedSubnets.Add(testSubnet1.ID()) + + // Add a subnet validator to the staker set + subnetValidatorNodeID := ids.NodeID(preFundedKeys[0].PublicKey().Address()) + // Starts after the corre + subnetVdr1StartTime := defaultValidateStartTime + subnetVdr1EndTime := defaultValidateStartTime.Add(defaultMinStakingDuration) + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetVdr1StartTime.Unix()), // Start time + uint64(subnetVdr1EndTime.Unix()), // end time + subnetValidatorNodeID, // Node ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + 0, + ) + + env.state.PutCurrentValidator(staker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + + // The above validator is now part of the staking set + + // Queue a staker that joins the staker set after the above validator leaves + subnetVdr2NodeID := ids.NodeID(preFundedKeys[1].PublicKey().Address()) + tx, err = env.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetVdr1EndTime.Add(time.Second).Unix()), // Start time + uint64(subnetVdr1EndTime.Add(time.Second).Add(defaultMinStakingDuration).Unix()), // end time + subnetVdr2NodeID, // Node ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker = state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) + + env.state.PutPendingValidator(staker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + + // The above validator is now in the pending staker set + + // Advance time to the first staker's end time. + env.clk.Set(subnetVdr1EndTime) + // build standard block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessStandardBlock, err := blocks.NewBanffStandardBlock( + subnetVdr1EndTime, + parentBlk.ID(), + parentBlk.Height()+1, + nil, // txs nulled to simplify test + ) + require.NoError(err) + block := env.blkManager.NewBlock(statelessStandardBlock) + + // update staker set + require.NoError(block.Verify()) + + blkStateMap := env.blkManager.(*manager).blkIDToState + updatedState := blkStateMap[block.ID()].onAcceptState + _, err = updatedState.GetCurrentValidator(testSubnet1.ID(), subnetValidatorNodeID) + require.ErrorIs(err, database.ErrNotFound) + + // Check VM Validators are removed successfully + require.NoError(block.Accept()) + require.False(env.config.Validators.Contains(testSubnet1.ID(), subnetVdr2NodeID)) + require.False(env.config.Validators.Contains(testSubnet1.ID(), subnetValidatorNodeID)) +} + +func TestBanffStandardBlockWhitelistedSubnet(t *testing.T) { + require := require.New(t) + + for _, whitelist := range []bool{true, false} { + t.Run(fmt.Sprintf("whitelisted %t", whitelist), func(ts *testing.T) { + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + if whitelist { + env.config.WhitelistedSubnets.Add(testSubnet1.ID()) + } + + // Add a subnet validator to the staker set + subnetValidatorNodeID := ids.NodeID(preFundedKeys[0].PublicKey().Address()) + + subnetVdr1StartTime := defaultGenesisTime.Add(1 * time.Minute) + subnetVdr1EndTime := defaultGenesisTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute) + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetVdr1StartTime.Unix()), // Start time + uint64(subnetVdr1EndTime.Unix()), // end time + subnetValidatorNodeID, // Node ID + testSubnet1.ID(), // Subnet ID + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, + ) + require.NoError(err) + + staker := state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) + + env.state.PutPendingValidator(staker) + env.state.AddTx(tx, status.Committed) + require.NoError(env.state.Commit()) + + // Advance time to the staker's start time. + env.clk.Set(subnetVdr1StartTime) + + // build standard block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessStandardBlock, err := blocks.NewBanffStandardBlock( + subnetVdr1StartTime, + parentBlk.ID(), + parentBlk.Height()+1, + nil, // txs nulled to simplify test + ) + require.NoError(err) + block := env.blkManager.NewBlock(statelessStandardBlock) + + // update staker set + require.NoError(block.Verify()) + require.NoError(block.Accept()) + require.Equal(whitelist, env.config.Validators.Contains(testSubnet1.ID(), subnetValidatorNodeID)) + }) + } +} + +func TestBanffStandardBlockDelegatorStakerWeight(t *testing.T) { + require := require.New(t) + env := newEnvironment(t, nil) + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.config.BanffTime = time.Time{} // activate Banff + + // Case: Timestamp is after next validator start time + // Add a pending validator + pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) + pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMaxStakingDuration) + nodeID := ids.GenerateTestNodeID() + rewardAddress := ids.GenerateTestShortID() + _, err := addPendingValidator( + env, + pendingValidatorStartTime, + pendingValidatorEndTime, + nodeID, + rewardAddress, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ) + require.NoError(err) + + // build standard block moving ahead chain time + preferredID := env.state.GetLastAccepted() + parentBlk, _, err := env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessStandardBlock, err := blocks.NewBanffStandardBlock( + pendingValidatorStartTime, + parentBlk.ID(), + parentBlk.Height()+1, + nil, // txs nulled to simplify test + ) + require.NoError(err) + block := env.blkManager.NewBlock(statelessStandardBlock) + require.NoError(block.Verify()) + require.NoError(block.Accept()) + require.NoError(env.state.Commit()) + + // Test validator weight before delegation + primarySet, ok := env.config.Validators.GetValidators(constants.PrimaryNetworkID) + require.True(ok) + vdrWeight, _ := primarySet.GetWeight(nodeID) + require.Equal(env.config.MinValidatorStake, vdrWeight) + + // Add delegator + pendingDelegatorStartTime := pendingValidatorStartTime.Add(1 * time.Second) + pendingDelegatorEndTime := pendingDelegatorStartTime.Add(1 * time.Second) + + addDelegatorTx, err := env.txBuilder.NewAddDelegatorTx( + env.config.MinDelegatorStake, + uint64(pendingDelegatorStartTime.Unix()), + uint64(pendingDelegatorEndTime.Unix()), + nodeID, + preFundedKeys[0].PublicKey().Address(), + []*crypto.PrivateKeySECP256K1R{ + preFundedKeys[0], + preFundedKeys[1], + preFundedKeys[4], + }, + ids.ShortEmpty, + ) + require.NoError(err) + + staker := state.NewPendingStaker( + addDelegatorTx.ID(), + addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), + ) + + env.state.PutPendingDelegator(staker) + env.state.AddTx(addDelegatorTx, status.Committed) + env.state.SetHeight( /*dummyHeight*/ uint64(1)) + require.NoError(env.state.Commit()) + + // Advance Time + preferredID = env.state.GetLastAccepted() + parentBlk, _, err = env.state.GetStatelessBlock(preferredID) + require.NoError(err) + statelessStandardBlock, err = blocks.NewBanffStandardBlock( + pendingDelegatorStartTime, + parentBlk.ID(), + parentBlk.Height()+1, + nil, // txs nulled to simplify test + ) + require.NoError(err) + block = env.blkManager.NewBlock(statelessStandardBlock) + require.NoError(block.Verify()) + require.NoError(block.Accept()) + require.NoError(env.state.Commit()) + + // Test validator weight after delegation + vdrWeight, _ = primarySet.GetWeight(nodeID) + require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight) +} diff --git a/avalanchego/vms/platformvm/blocks/executor/verifier.go b/avalanchego/vms/platformvm/blocks/executor/verifier.go index fc47eb99..bee7ac66 100644 --- a/avalanchego/vms/platformvm/blocks/executor/verifier.go +++ b/avalanchego/vms/platformvm/blocks/executor/verifier.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -19,8 +19,14 @@ import ( var ( _ blocks.Visitor = &verifier{} - errConflictingBatchTxs = errors.New("block contains conflicting transactions") - errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block") + errBanffBlockIssuedBeforeFork = errors.New("banff block issued before fork") + errApricotBlockIssuedAfterFork = errors.New("apricot block issued after fork") + errBanffProposalBlockWithMultipleTransactions = errors.New("BanffProposalBlock contains multiple transactions") + errBanffStandardBlockWithoutChanges = errors.New("BanffStandardBlock performs no state changes") + errChildBlockEarlierThanParent = errors.New("proposed timestamp before current chain time") + errConflictingBatchTxs = errors.New("block contains conflicting transactions") + errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block") + errOptionBlockTimestampNotMatchingParent = errors.New("option block proposed timestamp not matching parent block one") ) // verifier handles the logic for verifying a block. @@ -29,74 +35,151 @@ type verifier struct { txExecutorBackend *executor.Backend } -func (v *verifier) ProposalBlock(b *blocks.ProposalBlock) error { - blkID := b.ID() +func (v *verifier) BanffAbortBlock(b *blocks.BanffAbortBlock) error { + if err := v.banffOptionBlock(b); err != nil { + return err + } + return v.abortBlock(b) +} - if _, ok := v.blkIDToState[blkID]; ok { - // This block has already been verified. - return nil +func (v *verifier) BanffCommitBlock(b *blocks.BanffCommitBlock) error { + if err := v.banffOptionBlock(b); err != nil { + return err } + return v.commitBlock(b) +} - if err := v.verifyCommonBlock(&b.CommonBlock); err != nil { +func (v *verifier) BanffProposalBlock(b *blocks.BanffProposalBlock) error { + if len(b.Transactions) != 0 { + return errBanffProposalBlockWithMultipleTransactions + } + + if err := v.banffNonOptionBlock(b); err != nil { return err } - txExecutor := &executor.ProposalTxExecutor{ - Backend: v.txExecutorBackend, - ParentID: b.Parent(), - StateVersions: v, - Tx: b.Tx, + parentID := b.Parent() + onCommitState, err := state.NewDiff(parentID, v.backend) + if err != nil { + return err } - if err := b.Tx.Unsigned.Visit(txExecutor); err != nil { - txID := b.Tx.ID() - v.MarkDropped(txID, err.Error()) // cache tx as dropped + onAbortState, err := state.NewDiff(parentID, v.backend) + if err != nil { return err } - onCommitState := txExecutor.OnCommit - onCommitState.AddTx(b.Tx, status.Committed) + // Apply the changes, if any, from advancing the chain time. + nextChainTime := b.Timestamp() + changes, err := executor.AdvanceTimeTo( + v.txExecutorBackend, + onCommitState, + nextChainTime, + ) + if err != nil { + return err + } - onAbortState := txExecutor.OnAbort - onAbortState.AddTx(b.Tx, status.Aborted) + onCommitState.SetTimestamp(nextChainTime) + changes.Apply(onCommitState) - v.blkIDToState[blkID] = &blockState{ - statelessBlock: b, - proposalBlockState: proposalBlockState{ - onCommitState: onCommitState, - onAbortState: onAbortState, - initiallyPreferCommit: txExecutor.PrefersCommit, - }, + onAbortState.SetTimestamp(nextChainTime) + changes.Apply(onAbortState) - // It is safe to use [pb.onAbortState] here because the timestamp will - // never be modified by an Abort block. - timestamp: onAbortState.GetTimestamp(), + return v.proposalBlock(&b.ApricotProposalBlock, onCommitState, onAbortState) +} + +func (v *verifier) BanffStandardBlock(b *blocks.BanffStandardBlock) error { + if err := v.banffNonOptionBlock(b); err != nil { + return err } - v.Mempool.RemoveProposalTx(b.Tx) - return nil + parentID := b.Parent() + onAcceptState, err := state.NewDiff(parentID, v.backend) + if err != nil { + return err + } + + // Apply the changes, if any, from advancing the chain time. + nextChainTime := b.Timestamp() + changes, err := executor.AdvanceTimeTo( + v.txExecutorBackend, + onAcceptState, + nextChainTime, + ) + if err != nil { + return err + } + + // If this block doesn't perform any changes, then it should never have been + // issued. + if changes.Len() == 0 && len(b.Transactions) == 0 { + return errBanffStandardBlockWithoutChanges + } + + onAcceptState.SetTimestamp(nextChainTime) + changes.Apply(onAcceptState) + + return v.standardBlock(&b.ApricotStandardBlock, onAcceptState) } -func (v *verifier) AtomicBlock(b *blocks.AtomicBlock) error { - blkID := b.ID() +func (v *verifier) ApricotAbortBlock(b *blocks.ApricotAbortBlock) error { + if err := v.apricotCommonBlock(b); err != nil { + return err + } + return v.abortBlock(b) +} - if _, ok := v.blkIDToState[blkID]; ok { - // This block has already been verified. - return nil +func (v *verifier) ApricotCommitBlock(b *blocks.ApricotCommitBlock) error { + if err := v.apricotCommonBlock(b); err != nil { + return err } + return v.commitBlock(b) +} - if err := v.verifyCommonBlock(&b.CommonBlock); err != nil { +func (v *verifier) ApricotProposalBlock(b *blocks.ApricotProposalBlock) error { + if err := v.apricotCommonBlock(b); err != nil { return err } parentID := b.Parent() - parentState, ok := v.GetState(parentID) - if !ok { - return fmt.Errorf("could not retrieve state for %s, parent of %s", parentID, blkID) + onCommitState, err := state.NewDiff(parentID, v.backend) + if err != nil { + return err + } + onAbortState, err := state.NewDiff(parentID, v.backend) + if err != nil { + return err + } + + return v.proposalBlock(b, onCommitState, onAbortState) +} + +func (v *verifier) ApricotStandardBlock(b *blocks.ApricotStandardBlock) error { + if err := v.apricotCommonBlock(b); err != nil { + return err + } + + parentID := b.Parent() + onAcceptState, err := state.NewDiff(parentID, v) + if err != nil { + return err + } + + return v.standardBlock(b, onAcceptState) +} + +func (v *verifier) ApricotAtomicBlock(b *blocks.ApricotAtomicBlock) error { + // We call [commonBlock] here rather than [apricotCommonBlock] because below + // this check we perform the more strict check that ApricotPhase5 isn't + // activated. + if err := v.commonBlock(b); err != nil { + return err } + parentID := b.Parent() + currentTimestamp := v.getTimestamp(parentID) cfg := v.txExecutorBackend.Config - currentTimestamp := parentState.GetTimestamp() - if enbledAP5 := !currentTimestamp.Before(cfg.ApricotPhase5Time); enbledAP5 { + if cfg.IsApricotPhase5Activated(currentTimestamp) { return fmt.Errorf( "the chain timestamp (%d) is after the apricot phase 5 time (%d), hence atomic transactions should go through the standard block", currentTimestamp.Unix(), @@ -104,14 +187,14 @@ func (v *verifier) AtomicBlock(b *blocks.AtomicBlock) error { ) } - atomicExecutor := &executor.AtomicTxExecutor{ + atomicExecutor := executor.AtomicTxExecutor{ Backend: v.txExecutorBackend, ParentID: parentID, StateVersions: v, Tx: b.Tx, } - if err := b.Tx.Unsigned.Visit(atomicExecutor); err != nil { + if err := b.Tx.Unsigned.Visit(&atomicExecutor); err != nil { txID := b.Tx.ID() v.MarkDropped(txID, err.Error()) // cache tx as dropped return fmt.Errorf("tx %s failed semantic verification: %w", txID, err) @@ -123,49 +206,218 @@ func (v *verifier) AtomicBlock(b *blocks.AtomicBlock) error { return err } + blkID := b.ID() v.blkIDToState[blkID] = &blockState{ - statelessBlock: b, - onAcceptState: atomicExecutor.OnAccept, standardBlockState: standardBlockState{ inputs: atomicExecutor.Inputs, }, - atomicRequests: atomicExecutor.AtomicRequests, + statelessBlock: b, + onAcceptState: atomicExecutor.OnAccept, timestamp: atomicExecutor.OnAccept.GetTimestamp(), + atomicRequests: atomicExecutor.AtomicRequests, + } + + v.Mempool.Remove([]*txs.Tx{b.Tx}) + return nil +} + +func (v *verifier) banffOptionBlock(b blocks.BanffBlock) error { + if err := v.banffCommonBlock(b); err != nil { + return err + } + + // Banff option blocks must be uniquely generated from the + // BanffProposalBlock. This means that the timestamp must be + // standardized to a specific value. Therefore, we require the timestamp to + // be equal to the parents timestamp. + parentID := b.Parent() + parentBlkTime := v.getTimestamp(parentID) + blkTime := b.Timestamp() + if !blkTime.Equal(parentBlkTime) { + return fmt.Errorf( + "%w parent block timestamp (%s) option block timestamp (%s)", + errOptionBlockTimestampNotMatchingParent, + parentBlkTime, + blkTime, + ) + } + return nil +} + +func (v *verifier) banffNonOptionBlock(b blocks.BanffBlock) error { + if err := v.banffCommonBlock(b); err != nil { + return err + } + + parentID := b.Parent() + parentState, ok := v.GetState(parentID) + if !ok { + return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID) + } + + newChainTime := b.Timestamp() + parentChainTime := parentState.GetTimestamp() + if newChainTime.Before(parentChainTime) { + return fmt.Errorf( + "%w: proposed timestamp (%s), chain time (%s)", + errChildBlockEarlierThanParent, + newChainTime, + parentChainTime, + ) + } + + nextStakerChangeTime, err := executor.GetNextStakerChangeTime(parentState) + if err != nil { + return fmt.Errorf("could not verify block timestamp: %w", err) + } + + now := v.txExecutorBackend.Clk.Time() + return executor.VerifyNewChainTime( + newChainTime, + nextStakerChangeTime, + now, + ) +} + +func (v *verifier) banffCommonBlock(b blocks.BanffBlock) error { + timestamp := b.Timestamp() + if !v.txExecutorBackend.Config.IsBanffActivated(timestamp) { + return fmt.Errorf("%w: timestamp = %s", errBanffBlockIssuedBeforeFork, timestamp) + } + return v.commonBlock(b) +} + +func (v *verifier) apricotCommonBlock(b blocks.Block) error { + // We can use the parent timestamp here, because we are guaranteed that the + // parent was verified. Apricot blocks only update the timestamp with + // AdvanceTimeTxs. This means that this block's timestamp will be equal to + // the parent block's timestamp; unless this is a CommitBlock. In order for + // the timestamp of the CommitBlock to be after the Banff activation, + // the parent ApricotProposalBlock must include an AdvanceTimeTx with a + // timestamp after the Banff timestamp. This is verified not to occur + // during the verification of the ProposalBlock. + parentID := b.Parent() + timestamp := v.getTimestamp(parentID) + if v.txExecutorBackend.Config.IsBanffActivated(timestamp) { + return fmt.Errorf("%w: timestamp = %s", errApricotBlockIssuedAfterFork, timestamp) + } + return v.commonBlock(b) +} + +func (v *verifier) commonBlock(b blocks.Block) error { + parentID := b.Parent() + parent, err := v.GetBlock(parentID) + if err != nil { + return err } - v.Mempool.RemoveDecisionTxs([]*txs.Tx{b.Tx}) + expectedHeight := parent.Height() + 1 + height := b.Height() + if expectedHeight != height { + return fmt.Errorf( + "expected block to have height %d, but found %d", + expectedHeight, + height, + ) + } return nil } -func (v *verifier) StandardBlock(b *blocks.StandardBlock) error { +// abortBlock populates the state of this block if [nil] is returned +func (v *verifier) abortBlock(b blocks.Block) error { + parentID := b.Parent() + onAcceptState, ok := v.getOnAbortState(parentID) + if !ok { + return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID) + } + blkID := b.ID() + v.blkIDToState[blkID] = &blockState{ + statelessBlock: b, + onAcceptState: onAcceptState, + timestamp: onAcceptState.GetTimestamp(), + } + return nil +} - if _, ok := v.blkIDToState[blkID]; ok { - // This block has already been verified. - return nil +// commitBlock populates the state of this block if [nil] is returned +func (v *verifier) commitBlock(b blocks.Block) error { + parentID := b.Parent() + onAcceptState, ok := v.getOnCommitState(parentID) + if !ok { + return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID) } - blkState := &blockState{ + + blkID := b.ID() + v.blkIDToState[blkID] = &blockState{ statelessBlock: b, - atomicRequests: make(map[ids.ID]*atomic.Requests), + onAcceptState: onAcceptState, + timestamp: onAcceptState.GetTimestamp(), } + return nil +} - if err := v.verifyCommonBlock(&b.CommonBlock); err != nil { - return err +// proposalBlock populates the state of this block if [nil] is returned +func (v *verifier) proposalBlock( + b *blocks.ApricotProposalBlock, + onCommitState state.Diff, + onAbortState state.Diff, +) error { + txExecutor := executor.ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, + Backend: v.txExecutorBackend, + Tx: b.Tx, } - onAcceptState, err := state.NewDiff(b.Parent(), v) - if err != nil { + if err := b.Tx.Unsigned.Visit(&txExecutor); err != nil { + txID := b.Tx.ID() + v.MarkDropped(txID, err.Error()) // cache tx as dropped return err } + onCommitState.AddTx(b.Tx, status.Committed) + onAbortState.AddTx(b.Tx, status.Aborted) + + blkID := b.ID() + v.blkIDToState[blkID] = &blockState{ + proposalBlockState: proposalBlockState{ + onCommitState: onCommitState, + onAbortState: onAbortState, + initiallyPreferCommit: txExecutor.PrefersCommit, + }, + statelessBlock: b, + // It is safe to use [b.onAbortState] here because the timestamp will + // never be modified by an Apricot Abort block and the timestamp will + // always be the same as the Banff Proposal Block. + timestamp: onAbortState.GetTimestamp(), + } + + v.Mempool.Remove([]*txs.Tx{b.Tx}) + return nil +} + +// standardBlock populates the state of this block if [nil] is returned +func (v *verifier) standardBlock( + b *blocks.ApricotStandardBlock, + onAcceptState state.Diff, +) error { + blkState := &blockState{ + statelessBlock: b, + onAcceptState: onAcceptState, + timestamp: onAcceptState.GetTimestamp(), + atomicRequests: make(map[ids.ID]*atomic.Requests), + } + + // Finally we process the transactions funcs := make([]func(), 0, len(b.Transactions)) for _, tx := range b.Transactions { - txExecutor := &executor.StandardTxExecutor{ + txExecutor := executor.StandardTxExecutor{ Backend: v.txExecutorBackend, State: onAcceptState, Tx: tx, } - if err := tx.Unsigned.Visit(txExecutor); err != nil { + if err := tx.Unsigned.Visit(&txExecutor); err != nil { txID := tx.ID() v.MarkDropped(txID, err.Error()) // cache tx as dropped return err @@ -209,80 +461,10 @@ func (v *verifier) StandardBlock(b *blocks.StandardBlock) error { } } - blkState.timestamp = onAcceptState.GetTimestamp() - blkState.onAcceptState = onAcceptState - v.blkIDToState[blkID] = blkState - v.Mempool.RemoveDecisionTxs(b.Transactions) - return nil -} - -func (v *verifier) CommitBlock(b *blocks.CommitBlock) error { - blkID := b.ID() - - if _, ok := v.blkIDToState[blkID]; ok { - // This block has already been verified. - return nil - } - - if err := v.verifyCommonBlock(&b.CommonBlock); err != nil { - return err - } - - parentID := b.Parent() - parentState, ok := v.blkIDToState[parentID] - if !ok { - return fmt.Errorf("could not retrieve state for %s, parent of %s", parentID, blkID) - } - - onAcceptState := parentState.onCommitState - v.blkIDToState[blkID] = &blockState{ - statelessBlock: b, - timestamp: onAcceptState.GetTimestamp(), - onAcceptState: onAcceptState, - } - return nil -} - -func (v *verifier) AbortBlock(b *blocks.AbortBlock) error { blkID := b.ID() + v.blkIDToState[blkID] = blkState - if _, ok := v.blkIDToState[blkID]; ok { - // This block has already been verified. - return nil - } - - if err := v.verifyCommonBlock(&b.CommonBlock); err != nil { - return err - } - - parentID := b.Parent() - parentState, ok := v.blkIDToState[parentID] - if !ok { - return fmt.Errorf("could not retrieve state for %s, parent of %s", parentID, blkID) - } - - onAcceptState := parentState.onAbortState - v.blkIDToState[blkID] = &blockState{ - statelessBlock: b, - timestamp: onAcceptState.GetTimestamp(), - onAcceptState: onAcceptState, - } - return nil -} - -func (v *verifier) verifyCommonBlock(b *blocks.CommonBlock) error { - parentID := b.Parent() - parent, err := v.GetBlock(parentID) - if err != nil { - return err - } - if expectedHeight := parent.Height() + 1; expectedHeight != b.Height() { - return fmt.Errorf( - "expected block to have height %d, but found %d", - expectedHeight, - b.Height(), - ) - } + v.Mempool.Remove(b.Transactions) return nil } diff --git a/avalanchego/vms/platformvm/blocks/executor/verifier_test.go b/avalanchego/vms/platformvm/blocks/executor/verifier_test.go index 18679d17..b39292e4 100644 --- a/avalanchego/vms/platformvm/blocks/executor/verifier_test.go +++ b/avalanchego/vms/platformvm/blocks/executor/verifier_test.go @@ -12,9 +12,12 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/verify" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/config" @@ -34,40 +37,48 @@ func TestVerifierVisitProposalBlock(t *testing.T) { mempool := mempool.NewMockMempool(ctrl) parentID := ids.GenerateTestID() parentStatelessBlk := blocks.NewMockBlock(ctrl) - verifier := &verifier{ - txExecutorBackend: &executor.Backend{}, - backend: &backend{ - lastAccepted: parentID, - blkIDToState: map[ids.ID]*blockState{ - parentID: { - statelessBlock: parentStatelessBlk, - }, + parentOnAcceptState := state.NewMockDiff(ctrl) + timestamp := time.Now() + // One call for each of onCommitState and onAbortState. + parentOnAcceptState.EXPECT().GetTimestamp().Return(timestamp).Times(2) + + backend := &backend{ + lastAccepted: parentID, + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + onAcceptState: parentOnAcceptState, }, - Mempool: mempool, - state: s, - ctx: &snow.Context{ - Log: logging.NoLog{}, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: mockable.MaxTime, // banff is not activated }, + Clk: &mockable.Clock{}, }, + backend: backend, + } + manager := &manager{ + backend: backend, + verifier: verifier, } - onCommitState := state.NewMockDiff(ctrl) - onAbortState := state.NewMockDiff(ctrl) blkTx := txs.NewMockUnsignedTx(ctrl) - blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.ProposalTxExecutor{})).DoAndReturn( - func(e *executor.ProposalTxExecutor) error { - e.OnCommit = onCommitState - e.OnAbort = onAbortState - return nil - }, - ).Times(1) + blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.ProposalTxExecutor{})).Return(nil).Times(1) // We can't serialize [blkTx] because it isn't // registered with the blocks.Codec. // Serialize this block with a dummy tx // and replace it after creation with the mock tx. // TODO allow serialization of mock txs. - blk, err := blocks.NewProposalBlock( + apricotBlk, err := blocks.NewApricotProposalBlock( parentID, 2, &txs.Tx{ @@ -76,28 +87,33 @@ func TestVerifierVisitProposalBlock(t *testing.T) { }, ) require.NoError(err) - blk.Tx.Unsigned = blkTx + apricotBlk.Tx.Unsigned = blkTx // Set expectations for dependencies. - timestamp := time.Now() + tx := apricotBlk.Txs()[0] parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) - mempool.EXPECT().RemoveProposalTx(blk.Tx).Times(1) - onCommitState.EXPECT().AddTx(blk.Tx, status.Committed).Times(1) - onAbortState.EXPECT().AddTx(blk.Tx, status.Aborted).Times(1) - onAbortState.EXPECT().GetTimestamp().Return(timestamp).Times(1) + mempool.EXPECT().Remove([]*txs.Tx{tx}).Times(1) // Visit the block - err = verifier.ProposalBlock(blk) + blk := manager.NewBlock(apricotBlk) + err = blk.Verify() require.NoError(err) - require.Contains(verifier.backend.blkIDToState, blk.ID()) - gotBlkState := verifier.backend.blkIDToState[blk.ID()] - require.Equal(blk, gotBlkState.statelessBlock) - require.Equal(onCommitState, gotBlkState.onCommitState) - require.Equal(onAbortState, gotBlkState.onAbortState) + require.Contains(verifier.backend.blkIDToState, apricotBlk.ID()) + gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()] + require.Equal(apricotBlk, gotBlkState.statelessBlock) require.Equal(timestamp, gotBlkState.timestamp) + // Assert that the expected tx statuses are set. + _, gotStatus, err := gotBlkState.onCommitState.GetTx(tx.ID()) + require.NoError(err) + require.Equal(status.Committed, gotStatus) + + _, gotStatus, err = gotBlkState.onAbortState.GetTx(tx.ID()) + require.NoError(err) + require.Equal(status.Aborted, gotStatus) + // Visiting again should return nil without using dependencies. - err = verifier.ProposalBlock(blk) + err = blk.Verify() require.NoError(err) } @@ -113,25 +129,33 @@ func TestVerifierVisitAtomicBlock(t *testing.T) { parentStatelessBlk := blocks.NewMockBlock(ctrl) grandparentID := ids.GenerateTestID() parentState := state.NewMockDiff(ctrl) + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + onAcceptState: parentState, + }, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } verifier := &verifier{ txExecutorBackend: &executor.Backend{ Config: &config.Config{ ApricotPhase5Time: time.Now().Add(time.Hour), + BanffTime: mockable.MaxTime, // banff is not activated }, + Clk: &mockable.Clock{}, }, - backend: &backend{ - blkIDToState: map[ids.ID]*blockState{ - parentID: { - statelessBlock: parentStatelessBlk, - onAcceptState: parentState, - }, - }, - Mempool: mempool, - state: s, - ctx: &snow.Context{ - Log: logging.NoLog{}, - }, - }, + backend: backend, + } + manager := &manager{ + backend: backend, + verifier: verifier, } onAccept := state.NewMockDiff(ctrl) @@ -149,7 +173,7 @@ func TestVerifierVisitAtomicBlock(t *testing.T) { // Serialize this block with a dummy tx and replace it after creation with // the mock tx. // TODO allow serialization of mock txs. - blk, err := blocks.NewAtomicBlock( + apricotBlk, err := blocks.NewApricotAtomicBlock( parentID, 2, &txs.Tx{ @@ -158,29 +182,29 @@ func TestVerifierVisitAtomicBlock(t *testing.T) { }, ) require.NoError(err) - blk.Tx.Unsigned = blkTx + apricotBlk.Tx.Unsigned = blkTx // Set expectations for dependencies. timestamp := time.Now() - parentState.EXPECT().GetTimestamp().Return(timestamp).Times(1) parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) parentStatelessBlk.EXPECT().Parent().Return(grandparentID).Times(1) - mempool.EXPECT().RemoveDecisionTxs([]*txs.Tx{blk.Tx}).Times(1) - onAccept.EXPECT().AddTx(blk.Tx, status.Committed).Times(1) + mempool.EXPECT().Remove([]*txs.Tx{apricotBlk.Tx}).Times(1) + onAccept.EXPECT().AddTx(apricotBlk.Tx, status.Committed).Times(1) onAccept.EXPECT().GetTimestamp().Return(timestamp).Times(1) - err = verifier.AtomicBlock(blk) + blk := manager.NewBlock(apricotBlk) + err = blk.Verify() require.NoError(err) - require.Contains(verifier.backend.blkIDToState, blk.ID()) - gotBlkState := verifier.backend.blkIDToState[blk.ID()] - require.Equal(blk, gotBlkState.statelessBlock) + require.Contains(verifier.backend.blkIDToState, apricotBlk.ID()) + gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()] + require.Equal(apricotBlk, gotBlkState.statelessBlock) require.Equal(onAccept, gotBlkState.onAcceptState) require.Equal(inputs, gotBlkState.inputs) require.Equal(timestamp, gotBlkState.timestamp) // Visiting again should return nil without using dependencies. - err = verifier.AtomicBlock(blk) + err = blk.Verify() require.NoError(err) } @@ -195,25 +219,33 @@ func TestVerifierVisitStandardBlock(t *testing.T) { parentID := ids.GenerateTestID() parentStatelessBlk := blocks.NewMockBlock(ctrl) parentState := state.NewMockDiff(ctrl) + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + onAcceptState: parentState, + }, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } verifier := &verifier{ txExecutorBackend: &executor.Backend{ Config: &config.Config{ ApricotPhase5Time: time.Now().Add(time.Hour), + BanffTime: mockable.MaxTime, // banff is not activated }, + Clk: &mockable.Clock{}, }, - backend: &backend{ - blkIDToState: map[ids.ID]*blockState{ - parentID: { - statelessBlock: parentStatelessBlk, - onAcceptState: parentState, - }, - }, - Mempool: mempool, - state: s, - ctx: &snow.Context{ - Log: logging.NoLog{}, - }, - }, + backend: backend, + } + manager := &manager{ + backend: backend, + verifier: verifier, } blkTx := txs.NewMockUnsignedTx(ctrl) @@ -243,9 +275,9 @@ func TestVerifierVisitStandardBlock(t *testing.T) { // Serialize this block with a dummy tx // and replace it after creation with the mock tx. // TODO allow serialization of mock txs. - blk, err := blocks.NewStandardBlock( + apricotBlk, err := blocks.NewApricotStandardBlock( parentID, - 2, + 2, /*height*/ []*txs.Tx{ { Unsigned: &txs.AdvanceTimeTx{}, @@ -254,27 +286,27 @@ func TestVerifierVisitStandardBlock(t *testing.T) { }, ) require.NoError(err) - blk.Transactions[0].Unsigned = blkTx + apricotBlk.Transactions[0].Unsigned = blkTx // Set expectations for dependencies. timestamp := time.Now() parentState.EXPECT().GetTimestamp().Return(timestamp).Times(1) - parentState.EXPECT().GetCurrentSupply().Return(uint64(10000)).Times(1) parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) - mempool.EXPECT().RemoveDecisionTxs(blk.Transactions).Times(1) + mempool.EXPECT().Remove(apricotBlk.Txs()).Times(1) - err = verifier.StandardBlock(blk) + blk := manager.NewBlock(apricotBlk) + err = blk.Verify() require.NoError(err) // Assert expected state. - require.Contains(verifier.backend.blkIDToState, blk.ID()) - gotBlkState := verifier.backend.blkIDToState[blk.ID()] - require.Equal(blk, gotBlkState.statelessBlock) + require.Contains(verifier.backend.blkIDToState, apricotBlk.ID()) + gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()] + require.Equal(apricotBlk, gotBlkState.statelessBlock) require.Equal(ids.Set{}, gotBlkState.inputs) require.Equal(timestamp, gotBlkState.timestamp) // Visiting again should return nil without using dependencies. - err = verifier.StandardBlock(blk) + err = blk.Verify() require.NoError(err) } @@ -290,28 +322,39 @@ func TestVerifierVisitCommitBlock(t *testing.T) { parentStatelessBlk := blocks.NewMockBlock(ctrl) parentOnCommitState := state.NewMockDiff(ctrl) parentOnAbortState := state.NewMockDiff(ctrl) - verifier := &verifier{ - txExecutorBackend: &executor.Backend{}, - backend: &backend{ - blkIDToState: map[ids.ID]*blockState{ - parentID: { - statelessBlock: parentStatelessBlk, - proposalBlockState: proposalBlockState{ - onCommitState: parentOnCommitState, - onAbortState: parentOnAbortState, - }, - standardBlockState: standardBlockState{}, + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + proposalBlockState: proposalBlockState{ + onCommitState: parentOnCommitState, + onAbortState: parentOnAbortState, }, + standardBlockState: standardBlockState{}, }, - Mempool: mempool, - state: s, - ctx: &snow.Context{ - Log: logging.NoLog{}, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: mockable.MaxTime, // banff is not activated }, + Clk: &mockable.Clock{}, }, + backend: backend, + } + manager := &manager{ + backend: backend, + verifier: verifier, } - blk, err := blocks.NewCommitBlock( + apricotBlk, err := blocks.NewApricotCommitBlock( parentID, 2, ) @@ -325,17 +368,18 @@ func TestVerifierVisitCommitBlock(t *testing.T) { ) // Verify the block. - err = verifier.CommitBlock(blk) + blk := manager.NewBlock(apricotBlk) + err = blk.Verify() require.NoError(err) // Assert expected state. - require.Contains(verifier.backend.blkIDToState, blk.ID()) - gotBlkState := verifier.backend.blkIDToState[blk.ID()] + require.Contains(verifier.backend.blkIDToState, apricotBlk.ID()) + gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()] require.Equal(parentOnAbortState, gotBlkState.onAcceptState) require.Equal(timestamp, gotBlkState.timestamp) // Visiting again should return nil without using dependencies. - err = verifier.CommitBlock(blk) + err = blk.Verify() require.NoError(err) } @@ -351,28 +395,39 @@ func TestVerifierVisitAbortBlock(t *testing.T) { parentStatelessBlk := blocks.NewMockBlock(ctrl) parentOnCommitState := state.NewMockDiff(ctrl) parentOnAbortState := state.NewMockDiff(ctrl) - verifier := &verifier{ - txExecutorBackend: &executor.Backend{}, - backend: &backend{ - blkIDToState: map[ids.ID]*blockState{ - parentID: { - statelessBlock: parentStatelessBlk, - proposalBlockState: proposalBlockState{ - onCommitState: parentOnCommitState, - onAbortState: parentOnAbortState, - }, - standardBlockState: standardBlockState{}, + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + proposalBlockState: proposalBlockState{ + onCommitState: parentOnCommitState, + onAbortState: parentOnAbortState, }, + standardBlockState: standardBlockState{}, }, - Mempool: mempool, - state: s, - ctx: &snow.Context{ - Log: logging.NoLog{}, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: mockable.MaxTime, // banff is not activated }, + Clk: &mockable.Clock{}, }, + backend: backend, + } + manager := &manager{ + backend: backend, + verifier: verifier, } - blk, err := blocks.NewAbortBlock( + apricotBlk, err := blocks.NewApricotAbortBlock( parentID, 2, ) @@ -386,20 +441,249 @@ func TestVerifierVisitAbortBlock(t *testing.T) { ) // Verify the block. - err = verifier.AbortBlock(blk) + blk := manager.NewBlock(apricotBlk) + err = blk.Verify() require.NoError(err) // Assert expected state. - require.Contains(verifier.backend.blkIDToState, blk.ID()) - gotBlkState := verifier.backend.blkIDToState[blk.ID()] + require.Contains(verifier.backend.blkIDToState, apricotBlk.ID()) + gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()] require.Equal(parentOnAbortState, gotBlkState.onAcceptState) require.Equal(timestamp, gotBlkState.timestamp) // Visiting again should return nil without using dependencies. - err = verifier.AbortBlock(blk) + err = blk.Verify() require.NoError(err) } +// Assert that a block with an unverified parent fails verification. +func TestVerifyUnverifiedParent(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + mempool := mempool.NewMockMempool(ctrl) + parentID := ids.GenerateTestID() + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{}, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: mockable.MaxTime, // banff is not activated + }, + Clk: &mockable.Clock{}, + }, + backend: backend, + } + + blk, err := blocks.NewApricotAbortBlock(parentID /*not in memory or persisted state*/, 2 /*height*/) + require.NoError(err) + + // Set expectations for dependencies. + s.EXPECT().GetTimestamp().Return(time.Now()).Times(1) + s.EXPECT().GetStatelessBlock(parentID).Return(nil, choices.Unknown, database.ErrNotFound).Times(1) + + // Verify the block. + err = blk.Visit(verifier) + require.Error(err) +} + +func TestBanffAbortBlockTimestampChecks(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + now := defaultGenesisTime.Add(time.Hour) + + tests := []struct { + description string + parentTime time.Time + childTime time.Time + result error + }{ + { + description: "abort block timestamp matching parent's one", + parentTime: now, + childTime: now, + result: nil, + }, + { + description: "abort block timestamp before parent's one", + childTime: now.Add(-1 * time.Second), + parentTime: now, + result: errOptionBlockTimestampNotMatchingParent, + }, + { + description: "abort block timestamp after parent's one", + parentTime: now, + childTime: now.Add(time.Second), + result: errOptionBlockTimestampNotMatchingParent, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + require := require.New(t) + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + mempool := mempool.NewMockMempool(ctrl) + parentID := ids.GenerateTestID() + parentStatelessBlk := blocks.NewMockBlock(ctrl) + parentHeight := uint64(1) + + backend := &backend{ + blkIDToState: make(map[ids.ID]*blockState), + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: time.Time{}, // banff is activated + }, + Clk: &mockable.Clock{}, + }, + backend: backend, + } + + // build and verify child block + childHeight := parentHeight + 1 + statelessAbortBlk, err := blocks.NewBanffAbortBlock(test.childTime, parentID, childHeight) + require.NoError(err) + + // setup parent state + parentTime := defaultGenesisTime + s.EXPECT().GetLastAccepted().Return(parentID).Times(2) + s.EXPECT().GetTimestamp().Return(parentTime).Times(2) + + onCommitState, err := state.NewDiff(parentID, backend) + require.NoError(err) + onAbortState, err := state.NewDiff(parentID, backend) + require.NoError(err) + backend.blkIDToState[parentID] = &blockState{ + timestamp: test.parentTime, + statelessBlock: parentStatelessBlk, + proposalBlockState: proposalBlockState{ + onCommitState: onCommitState, + onAbortState: onAbortState, + }, + } + + // Set expectations for dependencies. + parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) + + err = statelessAbortBlk.Visit(verifier) + require.ErrorIs(err, test.result) + }) + } +} + +// TODO combine with TestApricotCommitBlockTimestampChecks +func TestBanffCommitBlockTimestampChecks(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + now := defaultGenesisTime.Add(time.Hour) + + tests := []struct { + description string + parentTime time.Time + childTime time.Time + result error + }{ + { + description: "commit block timestamp matching parent's one", + parentTime: now, + childTime: now, + result: nil, + }, + { + description: "commit block timestamp before parent's one", + childTime: now.Add(-1 * time.Second), + parentTime: now, + result: errOptionBlockTimestampNotMatchingParent, + }, + { + description: "commit block timestamp after parent's one", + parentTime: now, + childTime: now.Add(time.Second), + result: errOptionBlockTimestampNotMatchingParent, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + require := require.New(t) + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + mempool := mempool.NewMockMempool(ctrl) + parentID := ids.GenerateTestID() + parentStatelessBlk := blocks.NewMockBlock(ctrl) + parentHeight := uint64(1) + + backend := &backend{ + blkIDToState: make(map[ids.ID]*blockState), + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: time.Time{}, // banff is activated + }, + Clk: &mockable.Clock{}, + }, + backend: backend, + } + + // build and verify child block + childHeight := parentHeight + 1 + statelessCommitBlk, err := blocks.NewBanffCommitBlock(test.childTime, parentID, childHeight) + require.NoError(err) + + // setup parent state + parentTime := defaultGenesisTime + s.EXPECT().GetLastAccepted().Return(parentID).Times(2) + s.EXPECT().GetTimestamp().Return(parentTime).Times(2) + + onCommitState, err := state.NewDiff(parentID, backend) + require.NoError(err) + onAbortState, err := state.NewDiff(parentID, backend) + require.NoError(err) + backend.blkIDToState[parentID] = &blockState{ + timestamp: test.parentTime, + statelessBlock: parentStatelessBlk, + proposalBlockState: proposalBlockState{ + onCommitState: onCommitState, + onAbortState: onAbortState, + }, + } + + // Set expectations for dependencies. + parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) + + err = statelessCommitBlk.Visit(verifier) + require.ErrorIs(err, test.result) + }) + } +} + func TestVerifierVisitStandardBlockWithDuplicateInputs(t *testing.T) { require := require.New(t) ctrl := gomock.NewController(t) @@ -408,6 +692,7 @@ func TestVerifierVisitStandardBlockWithDuplicateInputs(t *testing.T) { // Create mocked dependencies. s := state.NewMockState(ctrl) mempool := mempool.NewMockMempool(ctrl) + grandParentID := ids.GenerateTestID() grandParentStatelessBlk := blocks.NewMockBlock(ctrl) grandParentState := state.NewMockDiff(ctrl) @@ -417,32 +702,36 @@ func TestVerifierVisitStandardBlockWithDuplicateInputs(t *testing.T) { atomicInputs := ids.Set{ ids.GenerateTestID(): struct{}{}, } + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{ + grandParentID: { + standardBlockState: standardBlockState{ + inputs: atomicInputs, + }, + statelessBlock: grandParentStatelessBlk, + onAcceptState: grandParentState, + }, + parentID: { + statelessBlock: parentStatelessBlk, + onAcceptState: parentState, + }, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } verifier := &verifier{ txExecutorBackend: &executor.Backend{ Config: &config.Config{ ApricotPhase5Time: time.Now().Add(time.Hour), + BanffTime: mockable.MaxTime, // banff is not activated }, + Clk: &mockable.Clock{}, }, - backend: &backend{ - blkIDToState: map[ids.ID]*blockState{ - grandParentID: { - standardBlockState: standardBlockState{ - inputs: atomicInputs, - }, - statelessBlock: grandParentStatelessBlk, - onAcceptState: grandParentState, - }, - parentID: { - statelessBlock: parentStatelessBlk, - onAcceptState: parentState, - }, - }, - Mempool: mempool, - state: s, - ctx: &snow.Context{ - Log: logging.NoLog{}, - }, - }, + backend: backend, } blkTx := txs.NewMockUnsignedTx(ctrl) @@ -468,11 +757,11 @@ func TestVerifierVisitStandardBlockWithDuplicateInputs(t *testing.T) { ).Times(1) // We can't serialize [blkTx] because it isn't - // regiestered with the blocks.Codec. + // registered with the blocks.Codec. // Serialize this block with a dummy tx // and replace it after creation with the mock tx. // TODO allow serialization of mock txs. - blk, err := blocks.NewStandardBlock( + blk, err := blocks.NewApricotStandardBlock( parentID, 2, []*txs.Tx{ @@ -489,14 +778,13 @@ func TestVerifierVisitStandardBlockWithDuplicateInputs(t *testing.T) { timestamp := time.Now() parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) parentState.EXPECT().GetTimestamp().Return(timestamp).Times(1) - parentState.EXPECT().GetCurrentSupply().Return(uint64(10000)).Times(1) parentStatelessBlk.EXPECT().Parent().Return(grandParentID).Times(1) - err = verifier.StandardBlock(blk) + err = verifier.ApricotStandardBlock(blk) require.ErrorIs(err, errConflictingParentTxs) } -func TestVerifierVisitStandardBlockWithProposalBlockParent(t *testing.T) { +func TestVerifierVisitApricotStandardBlockWithProposalBlockParent(t *testing.T) { require := require.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -508,41 +796,286 @@ func TestVerifierVisitStandardBlockWithProposalBlockParent(t *testing.T) { parentStatelessBlk := blocks.NewMockBlock(ctrl) parentOnCommitState := state.NewMockDiff(ctrl) parentOnAbortState := state.NewMockDiff(ctrl) + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + proposalBlockState: proposalBlockState{ + onCommitState: parentOnCommitState, + onAbortState: parentOnAbortState, + }, + standardBlockState: standardBlockState{}, + }, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } verifier := &verifier{ - txExecutorBackend: &executor.Backend{}, + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: mockable.MaxTime, // banff is not activated + }, + Clk: &mockable.Clock{}, + }, + backend: backend, + } + + blk, err := blocks.NewApricotStandardBlock( + parentID, + 2, + []*txs.Tx{ + { + Unsigned: &txs.AdvanceTimeTx{}, + Creds: []verify.Verifiable{}, + }, + }, + ) + require.NoError(err) + + parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) + + err = verifier.ApricotStandardBlock(blk) + require.ErrorIs(err, state.ErrMissingParentState) +} + +func TestVerifierVisitBanffStandardBlockWithProposalBlockParent(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + mempool := mempool.NewMockMempool(ctrl) + parentID := ids.GenerateTestID() + parentStatelessBlk := blocks.NewMockBlock(ctrl) + parentTime := time.Now() + parentOnCommitState := state.NewMockDiff(ctrl) + parentOnAbortState := state.NewMockDiff(ctrl) + + backend := &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + proposalBlockState: proposalBlockState{ + onCommitState: parentOnCommitState, + onAbortState: parentOnAbortState, + }, + standardBlockState: standardBlockState{}, + }, + }, + Mempool: mempool, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + } + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: time.Time{}, // banff is activated + }, + Clk: &mockable.Clock{}, + }, + backend: backend, + } + + blk, err := blocks.NewBanffStandardBlock( + parentTime.Add(time.Second), + parentID, + 2, + []*txs.Tx{ + { + Unsigned: &txs.AdvanceTimeTx{}, + Creds: []verify.Verifiable{}, + }, + }, + ) + require.NoError(err) + + parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) + + err = verifier.BanffStandardBlock(blk) + require.ErrorIs(err, state.ErrMissingParentState) +} + +func TestVerifierVisitApricotCommitBlockUnexpectedParentState(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + parentID := ids.GenerateTestID() + parentStatelessBlk := blocks.NewMockBlock(ctrl) + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: mockable.MaxTime, // banff is not activated + }, + Clk: &mockable.Clock{}, + }, backend: &backend{ blkIDToState: map[ids.ID]*blockState{ parentID: { statelessBlock: parentStatelessBlk, - proposalBlockState: proposalBlockState{ - onCommitState: parentOnCommitState, - onAbortState: parentOnAbortState, - }, - standardBlockState: standardBlockState{}, }, }, - Mempool: mempool, - state: s, + state: s, ctx: &snow.Context{ Log: logging.NoLog{}, }, }, } - blk, err := blocks.NewStandardBlock( + blk, err := blocks.NewApricotCommitBlock( parentID, 2, - []*txs.Tx{ - { - Unsigned: &txs.AdvanceTimeTx{}, - Creds: []verify.Verifiable{}, + ) + require.NoError(err) + + // Set expectations for dependencies. + parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) + + // Verify the block. + err = verifier.ApricotCommitBlock(blk) + require.ErrorIs(err, state.ErrMissingParentState) +} + +func TestVerifierVisitBanffCommitBlockUnexpectedParentState(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + parentID := ids.GenerateTestID() + parentStatelessBlk := blocks.NewMockBlock(ctrl) + timestamp := time.Unix(12345, 0) + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: time.Time{}, // banff is activated + }, + Clk: &mockable.Clock{}, + }, + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + timestamp: timestamp, + }, + }, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, }, }, + } + + blk, err := blocks.NewBanffCommitBlock( + timestamp, + parentID, + 2, ) require.NoError(err) + // Set expectations for dependencies. parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) - err = verifier.StandardBlock(blk) + // Verify the block. + err = verifier.BanffCommitBlock(blk) + require.ErrorIs(err, state.ErrMissingParentState) +} + +func TestVerifierVisitApricotAbortBlockUnexpectedParentState(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + parentID := ids.GenerateTestID() + parentStatelessBlk := blocks.NewMockBlock(ctrl) + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: mockable.MaxTime, // banff is not activated + }, + Clk: &mockable.Clock{}, + }, + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + }, + }, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + }, + } + + blk, err := blocks.NewApricotAbortBlock( + parentID, + 2, + ) + require.NoError(err) + + // Set expectations for dependencies. + parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) + + // Verify the block. + err = verifier.ApricotAbortBlock(blk) + require.ErrorIs(err, state.ErrMissingParentState) +} + +func TestVerifierVisitBanffAbortBlockUnexpectedParentState(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create mocked dependencies. + s := state.NewMockState(ctrl) + parentID := ids.GenerateTestID() + parentStatelessBlk := blocks.NewMockBlock(ctrl) + timestamp := time.Unix(12345, 0) + verifier := &verifier{ + txExecutorBackend: &executor.Backend{ + Config: &config.Config{ + BanffTime: time.Time{}, // banff is activated + }, + Clk: &mockable.Clock{}, + }, + backend: &backend{ + blkIDToState: map[ids.ID]*blockState{ + parentID: { + statelessBlock: parentStatelessBlk, + timestamp: timestamp, + }, + }, + state: s, + ctx: &snow.Context{ + Log: logging.NoLog{}, + }, + }, + } + + blk, err := blocks.NewBanffAbortBlock( + timestamp, + parentID, + 2, + ) + require.NoError(err) + + // Set expectations for dependencies. + parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) + + // Verify the block. + err = verifier.BanffAbortBlock(blk) require.ErrorIs(err, state.ErrMissingParentState) } diff --git a/avalanchego/vms/platformvm/blocks/mock_block.go b/avalanchego/vms/platformvm/blocks/mock_block.go index e81ba0a8..8f88c68e 100644 --- a/avalanchego/vms/platformvm/blocks/mock_block.go +++ b/avalanchego/vms/platformvm/blocks/mock_block.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: vms/platformvm/blocks/block.go +// Source: github.com/ava-labs/avalanchego/vms/platformvm/blocks (interfaces: Block) // Package blocks is a generated GoMock package. package blocks @@ -8,6 +8,7 @@ import ( reflect "reflect" ids "github.com/ava-labs/avalanchego/ids" + snow "github.com/ava-labs/avalanchego/snow" txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" gomock "github.com/golang/mock/gomock" ) @@ -77,6 +78,18 @@ func (mr *MockBlockMockRecorder) ID() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockBlock)(nil).ID)) } +// InitCtx mocks base method. +func (m *MockBlock) InitCtx(arg0 *snow.Context) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "InitCtx", arg0) +} + +// InitCtx indicates an expected call of InitCtx. +func (mr *MockBlockMockRecorder) InitCtx(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitCtx", reflect.TypeOf((*MockBlock)(nil).InitCtx), arg0) +} + // Parent mocks base method. func (m *MockBlock) Parent() ids.ID { m.ctrl.T.Helper() @@ -106,29 +119,29 @@ func (mr *MockBlockMockRecorder) Txs() *gomock.Call { } // Visit mocks base method. -func (m *MockBlock) Visit(visitor Visitor) error { +func (m *MockBlock) Visit(arg0 Visitor) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Visit", visitor) + ret := m.ctrl.Call(m, "Visit", arg0) ret0, _ := ret[0].(error) return ret0 } // Visit indicates an expected call of Visit. -func (mr *MockBlockMockRecorder) Visit(visitor interface{}) *gomock.Call { +func (mr *MockBlockMockRecorder) Visit(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Visit", reflect.TypeOf((*MockBlock)(nil).Visit), visitor) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Visit", reflect.TypeOf((*MockBlock)(nil).Visit), arg0) } // initialize mocks base method. -func (m *MockBlock) initialize(bytes []byte) error { +func (m *MockBlock) initialize(arg0 []byte) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "initialize", bytes) + ret := m.ctrl.Call(m, "initialize", arg0) ret0, _ := ret[0].(error) return ret0 } // initialize indicates an expected call of initialize. -func (mr *MockBlockMockRecorder) initialize(bytes interface{}) *gomock.Call { +func (mr *MockBlockMockRecorder) initialize(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "initialize", reflect.TypeOf((*MockBlock)(nil).initialize), bytes) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "initialize", reflect.TypeOf((*MockBlock)(nil).initialize), arg0) } diff --git a/avalanchego/vms/platformvm/blocks/option_blocks.go b/avalanchego/vms/platformvm/blocks/option_blocks.go deleted file mode 100644 index b633c6df..00000000 --- a/avalanchego/vms/platformvm/blocks/option_blocks.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package blocks - -import ( - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" -) - -var ( - _ Block = &AbortBlock{} - _ Block = &CommitBlock{} -) - -type AbortBlock struct { - CommonBlock `serialize:"true"` -} - -func (ab *AbortBlock) initialize(bytes []byte) error { - ab.CommonBlock.initialize(bytes) - return nil -} - -func (ab *AbortBlock) Txs() []*txs.Tx { return nil } - -func (ab *AbortBlock) Visit(v Visitor) error { - return v.AbortBlock(ab) -} - -func NewAbortBlock( - parentID ids.ID, - height uint64, -) (*AbortBlock, error) { - blk := &AbortBlock{ - CommonBlock: CommonBlock{ - PrntID: parentID, - Hght: height, - }, - } - return blk, initialize(blk) -} - -type CommitBlock struct { - CommonBlock `serialize:"true"` -} - -func (cb *CommitBlock) initialize(bytes []byte) error { - cb.CommonBlock.initialize(bytes) - return nil -} - -func (cb *CommitBlock) Txs() []*txs.Tx { return nil } - -func (cb *CommitBlock) Visit(v Visitor) error { - return v.CommitBlock(cb) -} - -func NewCommitBlock( - parentID ids.ID, - height uint64, -) (*CommitBlock, error) { - blk := &CommitBlock{ - CommonBlock: CommonBlock{ - PrntID: parentID, - Hght: height, - }, - } - return blk, initialize(blk) -} diff --git a/avalanchego/vms/platformvm/blocks/parse.go b/avalanchego/vms/platformvm/blocks/parse.go index 8feb145d..a80ec3d4 100644 --- a/avalanchego/vms/platformvm/blocks/parse.go +++ b/avalanchego/vms/platformvm/blocks/parse.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks diff --git a/avalanchego/vms/platformvm/blocks/parse_test.go b/avalanchego/vms/platformvm/blocks/parse_test.go index 898daed4..5454254d 100644 --- a/avalanchego/vms/platformvm/blocks/parse_test.go +++ b/avalanchego/vms/platformvm/blocks/parse_test.go @@ -1,10 +1,11 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks import ( "testing" + "time" "github.com/stretchr/testify/require" @@ -18,9 +19,10 @@ import ( var preFundedKeys = crypto.BuildTestKeys() -func TestStandardBlock(t *testing.T) { - // check standard block can be built and parsed +func TestStandardBlocks(t *testing.T) { + // check Apricot standard block can be built and parsed require := require.New(t) + blkTimestamp := time.Now() parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} height := uint64(2022) txs, err := testDecisionTxs() @@ -28,32 +30,52 @@ func TestStandardBlock(t *testing.T) { for _, cdc := range []codec.Manager{Codec, GenesisCodec} { // build block - standardBlk, err := NewStandardBlock( - parentID, - height, - txs, - ) + apricotStandardBlk, err := NewApricotStandardBlock(parentID, height, txs) require.NoError(err) // parse block - parsed, err := Parse(cdc, standardBlk.Bytes()) + parsed, err := Parse(cdc, apricotStandardBlk.Bytes()) require.NoError(err) // compare content - require.Equal(standardBlk.ID(), parsed.ID()) - require.Equal(standardBlk.Bytes(), parsed.Bytes()) - require.Equal(standardBlk.Parent(), parsed.Parent()) - require.Equal(standardBlk.Height(), parsed.Height()) + require.Equal(apricotStandardBlk.ID(), parsed.ID()) + require.Equal(apricotStandardBlk.Bytes(), parsed.Bytes()) + require.Equal(apricotStandardBlk.Parent(), parsed.Parent()) + require.Equal(apricotStandardBlk.Height(), parsed.Height()) + + _, ok := parsed.(*ApricotStandardBlock) + require.True(ok) + require.Equal(txs, parsed.Txs()) + + // check that banff standard block can be built and parsed + banffStandardBlk, err := NewBanffStandardBlock(blkTimestamp, parentID, height, txs) + require.NoError(err) + + // parse block + parsed, err = Parse(cdc, banffStandardBlk.Bytes()) + require.NoError(err) - parsedStandardBlk, ok := parsed.(*StandardBlock) + // compare content + require.Equal(banffStandardBlk.ID(), parsed.ID()) + require.Equal(banffStandardBlk.Bytes(), parsed.Bytes()) + require.Equal(banffStandardBlk.Parent(), parsed.Parent()) + require.Equal(banffStandardBlk.Height(), parsed.Height()) + parsedBanffStandardBlk, ok := parsed.(*BanffStandardBlock) require.True(ok) - require.Equal(txs, parsedStandardBlk.Transactions) + require.Equal(txs, parsedBanffStandardBlk.Txs()) + + // timestamp check for banff blocks only + require.Equal(banffStandardBlk.Timestamp(), parsedBanffStandardBlk.Timestamp()) + + // backward compatibility check + require.Equal(parsed.Txs(), parsedBanffStandardBlk.Txs()) } } -func TestProposalBlock(t *testing.T) { - // check proposal block can be built and parsed +func TestProposalBlocks(t *testing.T) { + // check Apricot proposal block can be built and parsed require := require.New(t) + blkTimestamp := time.Now() parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} height := uint64(2022) tx, err := testProposalTx() @@ -61,7 +83,7 @@ func TestProposalBlock(t *testing.T) { for _, cdc := range []codec.Manager{Codec, GenesisCodec} { // build block - proposalBlk, err := NewProposalBlock( + apricotProposalBlk, err := NewApricotProposalBlock( parentID, height, tx, @@ -69,64 +91,132 @@ func TestProposalBlock(t *testing.T) { require.NoError(err) // parse block - parsed, err := Parse(cdc, proposalBlk.Bytes()) + parsed, err := Parse(cdc, apricotProposalBlk.Bytes()) require.NoError(err) // compare content - require.Equal(proposalBlk.ID(), parsed.ID()) - require.Equal(proposalBlk.Bytes(), parsed.Bytes()) - require.Equal(proposalBlk.Parent(), parsed.Parent()) - require.Equal(proposalBlk.Height(), parsed.Height()) + require.Equal(apricotProposalBlk.ID(), parsed.ID()) + require.Equal(apricotProposalBlk.Bytes(), parsed.Bytes()) + require.Equal(apricotProposalBlk.Parent(), parsed.Parent()) + require.Equal(apricotProposalBlk.Height(), parsed.Height()) - parsedProposalBlk, ok := parsed.(*ProposalBlock) + parsedApricotProposalBlk, ok := parsed.(*ApricotProposalBlock) require.True(ok) - require.Equal(tx, parsedProposalBlk.Tx) + require.Equal([]*txs.Tx{tx}, parsedApricotProposalBlk.Txs()) + + // check that banff proposal block can be built and parsed + banffProposalBlk, err := NewBanffProposalBlock( + blkTimestamp, + parentID, + height, + tx, + ) + require.NoError(err) + + // parse block + parsed, err = Parse(cdc, banffProposalBlk.Bytes()) + require.NoError(err) + + // compare content + require.Equal(banffProposalBlk.ID(), parsed.ID()) + require.Equal(banffProposalBlk.Bytes(), parsed.Bytes()) + require.Equal(banffProposalBlk.Parent(), banffProposalBlk.Parent()) + require.Equal(banffProposalBlk.Height(), parsed.Height()) + parsedBanffProposalBlk, ok := parsed.(*BanffProposalBlock) + require.True(ok) + require.Equal([]*txs.Tx{tx}, parsedBanffProposalBlk.Txs()) + + // timestamp check for banff blocks only + require.Equal(banffProposalBlk.Timestamp(), parsedBanffProposalBlk.Timestamp()) + + // backward compatibility check + require.Equal(parsedApricotProposalBlk.Txs(), parsedBanffProposalBlk.Txs()) } } func TestCommitBlock(t *testing.T) { - // check commit block can be built and parsed + // check Apricot commit block can be built and parsed require := require.New(t) + blkTimestamp := time.Now() parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} height := uint64(2022) for _, cdc := range []codec.Manager{Codec, GenesisCodec} { // build block - commitBlk, err := NewCommitBlock(parentID, height) + apricotCommitBlk, err := NewApricotCommitBlock(parentID, height) + require.NoError(err) + + // parse block + parsed, err := Parse(cdc, apricotCommitBlk.Bytes()) + require.NoError(err) + + // compare content + require.Equal(apricotCommitBlk.ID(), parsed.ID()) + require.Equal(apricotCommitBlk.Bytes(), parsed.Bytes()) + require.Equal(apricotCommitBlk.Parent(), parsed.Parent()) + require.Equal(apricotCommitBlk.Height(), parsed.Height()) + + // check that banff commit block can be built and parsed + banffCommitBlk, err := NewBanffCommitBlock(blkTimestamp, parentID, height) require.NoError(err) // parse block - parsed, err := Parse(cdc, commitBlk.Bytes()) + parsed, err = Parse(cdc, banffCommitBlk.Bytes()) require.NoError(err) // compare content - require.Equal(commitBlk.ID(), parsed.ID()) - require.Equal(commitBlk.Bytes(), parsed.Bytes()) - require.Equal(commitBlk.Parent(), parsed.Parent()) - require.Equal(commitBlk.Height(), parsed.Height()) + require.Equal(banffCommitBlk.ID(), parsed.ID()) + require.Equal(banffCommitBlk.Bytes(), parsed.Bytes()) + require.Equal(banffCommitBlk.Parent(), banffCommitBlk.Parent()) + require.Equal(banffCommitBlk.Height(), parsed.Height()) + + // timestamp check for banff blocks only + parsedBanffCommitBlk, ok := parsed.(*BanffCommitBlock) + require.True(ok) + require.Equal(banffCommitBlk.Timestamp(), parsedBanffCommitBlk.Timestamp()) } } func TestAbortBlock(t *testing.T) { - // check abort block can be built and parsed + // check Apricot abort block can be built and parsed require := require.New(t) + blkTimestamp := time.Now() parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} height := uint64(2022) for _, cdc := range []codec.Manager{Codec, GenesisCodec} { // build block - abortBlk, err := NewAbortBlock(parentID, height) + apricotAbortBlk, err := NewApricotAbortBlock(parentID, height) require.NoError(err) // parse block - parsed, err := Parse(cdc, abortBlk.Bytes()) + parsed, err := Parse(cdc, apricotAbortBlk.Bytes()) require.NoError(err) // compare content - require.Equal(abortBlk.ID(), parsed.ID()) - require.Equal(abortBlk.Bytes(), parsed.Bytes()) - require.Equal(abortBlk.Parent(), parsed.Parent()) - require.Equal(abortBlk.Height(), parsed.Height()) + require.Equal(apricotAbortBlk.ID(), parsed.ID()) + require.Equal(apricotAbortBlk.Bytes(), parsed.Bytes()) + require.Equal(apricotAbortBlk.Parent(), parsed.Parent()) + require.Equal(apricotAbortBlk.Height(), parsed.Height()) + + // check that banff abort block can be built and parsed + banffAbortBlk, err := NewBanffAbortBlock(blkTimestamp, parentID, height) + require.NoError(err) + + // parse block + parsed, err = Parse(cdc, banffAbortBlk.Bytes()) + require.NoError(err) + + // compare content + require.Equal(banffAbortBlk.ID(), parsed.ID()) + require.Equal(banffAbortBlk.Bytes(), parsed.Bytes()) + require.Equal(banffAbortBlk.Parent(), banffAbortBlk.Parent()) + require.Equal(banffAbortBlk.Height(), parsed.Height()) + + // timestamp check for banff blocks only + parsedBanffAbortBlk, ok := parsed.(*BanffAbortBlock) + require.True(ok) + require.Equal(banffAbortBlk.Timestamp(), parsedBanffAbortBlk.Timestamp()) } } @@ -140,7 +230,7 @@ func TestAtomicBlock(t *testing.T) { for _, cdc := range []codec.Manager{Codec, GenesisCodec} { // build block - atomicBlk, err := NewAtomicBlock( + atomicBlk, err := NewApricotAtomicBlock( parentID, height, tx, @@ -157,9 +247,9 @@ func TestAtomicBlock(t *testing.T) { require.Equal(atomicBlk.Parent(), parsed.Parent()) require.Equal(atomicBlk.Height(), parsed.Height()) - parsedAtomicBlk, ok := parsed.(*AtomicBlock) + parsedAtomicBlk, ok := parsed.(*ApricotAtomicBlock) require.True(ok) - require.Equal(tx, parsedAtomicBlk.Tx) + require.Equal([]*txs.Tx{tx}, parsedAtomicBlk.Txs()) } } @@ -210,7 +300,7 @@ func testAtomicTx() (*txs.Tx, error) { func testDecisionTxs() ([]*txs.Tx, error) { countTxs := 2 - txes := make([]*txs.Tx, 0, countTxs) + decisionTxs := make([]*txs.Tx, 0, countTxs) for i := 0; i < countTxs; i++ { // Create the tx utx := &txs.CreateChainTx{ @@ -253,9 +343,9 @@ func testDecisionTxs() ([]*txs.Tx, error) { if err != nil { return nil, err } - txes = append(txes, tx) + decisionTxs = append(decisionTxs, tx) } - return txes, nil + return decisionTxs, nil } func testProposalTx() (*txs.Tx, error) { diff --git a/avalanchego/vms/platformvm/blocks/proposal_block.go b/avalanchego/vms/platformvm/blocks/proposal_block.go index 4d16644a..354d1c0b 100644 --- a/avalanchego/vms/platformvm/blocks/proposal_block.go +++ b/avalanchego/vms/platformvm/blocks/proposal_block.go @@ -1,44 +1,88 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks import ( "fmt" + "time" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) -var _ Block = &ProposalBlock{} +var ( + _ BanffBlock = &BanffProposalBlock{} + _ Block = &ApricotProposalBlock{} +) -// As is, this is duplication of atomic block. But let's tolerate some code duplication for now -type ProposalBlock struct { - CommonBlock `serialize:"true"` +type BanffProposalBlock struct { + Time uint64 `serialize:"true" json:"time"` + // Transactions is currently unused. This is populated so that introducing + // them in the future will not require a codec change. + // + // TODO: when Transactions is used, we must correctly verify and apply their + // changes. + Transactions []*txs.Tx `serialize:"true" json:"-"` + ApricotProposalBlock `serialize:"true"` +} + +func (b *BanffProposalBlock) InitCtx(ctx *snow.Context) { + for _, tx := range b.Transactions { + tx.Unsigned.InitCtx(ctx) + } + b.ApricotProposalBlock.InitCtx(ctx) +} + +func (b *BanffProposalBlock) Timestamp() time.Time { return time.Unix(int64(b.Time), 0) } +func (b *BanffProposalBlock) Visit(v Visitor) error { return v.BanffProposalBlock(b) } + +func NewBanffProposalBlock( + timestamp time.Time, + parentID ids.ID, + height uint64, + tx *txs.Tx, +) (*BanffProposalBlock, error) { + blk := &BanffProposalBlock{ + Time: uint64(timestamp.Unix()), + ApricotProposalBlock: ApricotProposalBlock{ + CommonBlock: CommonBlock{ + PrntID: parentID, + Hght: height, + }, + Tx: tx, + }, + } + return blk, initialize(blk) +} - Tx *txs.Tx `serialize:"true" json:"tx"` +type ApricotProposalBlock struct { + CommonBlock `serialize:"true"` + Tx *txs.Tx `serialize:"true" json:"tx"` } -func (pb *ProposalBlock) initialize(bytes []byte) error { - pb.CommonBlock.initialize(bytes) - if err := pb.Tx.Sign(txs.Codec, nil); err != nil { +func (b *ApricotProposalBlock) initialize(bytes []byte) error { + b.CommonBlock.initialize(bytes) + if err := b.Tx.Sign(txs.Codec, nil); err != nil { return fmt.Errorf("failed to initialize tx: %w", err) } return nil } -func (pb *ProposalBlock) Txs() []*txs.Tx { return []*txs.Tx{pb.Tx} } - -func (pb *ProposalBlock) Visit(v Visitor) error { - return v.ProposalBlock(pb) +func (b *ApricotProposalBlock) InitCtx(ctx *snow.Context) { + b.Tx.Unsigned.InitCtx(ctx) } -func NewProposalBlock( +func (b *ApricotProposalBlock) Txs() []*txs.Tx { return []*txs.Tx{b.Tx} } +func (b *ApricotProposalBlock) Visit(v Visitor) error { return v.ApricotProposalBlock(b) } + +func NewApricotProposalBlock( parentID ids.ID, height uint64, tx *txs.Tx, -) (*ProposalBlock, error) { - blk := &ProposalBlock{ +) (*ApricotProposalBlock, error) { + blk := &ApricotProposalBlock{ CommonBlock: CommonBlock{ PrntID: parentID, Hght: height, diff --git a/avalanchego/vms/platformvm/blocks/proposal_block_test.go b/avalanchego/vms/platformvm/blocks/proposal_block_test.go new file mode 100644 index 00000000..7e5a5219 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/proposal_block_test.go @@ -0,0 +1,101 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package blocks + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +func TestNewBanffProposalBlock(t *testing.T) { + require := require.New(t) + + timestamp := time.Now().Truncate(time.Second) + parentID := ids.GenerateTestID() + height := uint64(1337) + + tx := &txs.Tx{ + Unsigned: &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{ + BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{}, + Outs: []*avax.TransferableOutput{}, + }, + }, + StakeOuts: []*avax.TransferableOutput{}, + Validator: validator.Validator{}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{}, + }, + }, + Creds: []verify.Verifiable{}, + } + require.NoError(tx.Sign(txs.Codec, nil)) + + blk, err := NewBanffProposalBlock( + timestamp, + parentID, + height, + tx, + ) + require.NoError(err) + + // Make sure the block and tx are initialized + require.NotNil(blk.Bytes()) + require.NotNil(blk.Tx.Bytes()) + require.NotEqual(ids.Empty, blk.Tx.ID()) + require.Equal(tx.Bytes(), blk.Tx.Bytes()) + require.Equal(timestamp, blk.Timestamp()) + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} + +func TestNewApricotProposalBlock(t *testing.T) { + require := require.New(t) + + parentID := ids.GenerateTestID() + height := uint64(1337) + + tx := &txs.Tx{ + Unsigned: &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{ + BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{}, + Outs: []*avax.TransferableOutput{}, + }, + }, + StakeOuts: []*avax.TransferableOutput{}, + Validator: validator.Validator{}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{}, + }, + }, + Creds: []verify.Verifiable{}, + } + require.NoError(tx.Sign(txs.Codec, nil)) + + blk, err := NewApricotProposalBlock( + parentID, + height, + tx, + ) + require.NoError(err) + + // Make sure the block and tx are initialized + require.NotNil(blk.Bytes()) + require.NotNil(blk.Tx.Bytes()) + require.NotEqual(ids.Empty, blk.Tx.ID()) + require.Equal(tx.Bytes(), blk.Tx.Bytes()) + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} diff --git a/avalanchego/vms/platformvm/blocks/standard_block.go b/avalanchego/vms/platformvm/blocks/standard_block.go index 478f3ad2..dba0a857 100644 --- a/avalanchego/vms/platformvm/blocks/standard_block.go +++ b/avalanchego/vms/platformvm/blocks/standard_block.go @@ -1,50 +1,84 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package blocks import ( "fmt" + "time" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) -var _ Block = &StandardBlock{} +var ( + _ BanffBlock = &BanffStandardBlock{} + _ Block = &ApricotStandardBlock{} +) + +type BanffStandardBlock struct { + Time uint64 `serialize:"true" json:"time"` + ApricotStandardBlock `serialize:"true"` +} -type StandardBlock struct { - CommonBlock `serialize:"true"` +func (b *BanffStandardBlock) Timestamp() time.Time { return time.Unix(int64(b.Time), 0) } +func (b *BanffStandardBlock) Visit(v Visitor) error { return v.BanffStandardBlock(b) } +func NewBanffStandardBlock( + timestamp time.Time, + parentID ids.ID, + height uint64, + txs []*txs.Tx, +) (*BanffStandardBlock, error) { + blk := &BanffStandardBlock{ + Time: uint64(timestamp.Unix()), + ApricotStandardBlock: ApricotStandardBlock{ + CommonBlock: CommonBlock{ + PrntID: parentID, + Hght: height, + }, + Transactions: txs, + }, + } + return blk, initialize(blk) +} + +type ApricotStandardBlock struct { + CommonBlock `serialize:"true"` Transactions []*txs.Tx `serialize:"true" json:"txs"` } -func (sb *StandardBlock) initialize(bytes []byte) error { - sb.CommonBlock.initialize(bytes) - for _, tx := range sb.Transactions { +func (b *ApricotStandardBlock) initialize(bytes []byte) error { + b.CommonBlock.initialize(bytes) + for _, tx := range b.Transactions { if err := tx.Sign(txs.Codec, nil); err != nil { - return fmt.Errorf("failed to initialize tx: %w", err) + return fmt.Errorf("failed to sign block: %w", err) } } return nil } -func (sb *StandardBlock) Txs() []*txs.Tx { return sb.Transactions } - -func (sb *StandardBlock) Visit(v Visitor) error { - return v.StandardBlock(sb) +func (b *ApricotStandardBlock) InitCtx(ctx *snow.Context) { + for _, tx := range b.Transactions { + tx.Unsigned.InitCtx(ctx) + } } -func NewStandardBlock( +func (b *ApricotStandardBlock) Txs() []*txs.Tx { return b.Transactions } +func (b *ApricotStandardBlock) Visit(v Visitor) error { return v.ApricotStandardBlock(b) } + +func NewApricotStandardBlock( parentID ids.ID, height uint64, - txes []*txs.Tx, -) (*StandardBlock, error) { - blk := &StandardBlock{ + txs []*txs.Tx, +) (*ApricotStandardBlock, error) { + blk := &ApricotStandardBlock{ CommonBlock: CommonBlock{ PrntID: parentID, Hght: height, }, - Transactions: txes, + Transactions: txs, } return blk, initialize(blk) } diff --git a/avalanchego/vms/platformvm/blocks/standard_block_test.go b/avalanchego/vms/platformvm/blocks/standard_block_test.go new file mode 100644 index 00000000..ac9578e2 --- /dev/null +++ b/avalanchego/vms/platformvm/blocks/standard_block_test.go @@ -0,0 +1,101 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package blocks + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +func TestNewBanffStandardBlock(t *testing.T) { + require := require.New(t) + + timestamp := time.Now().Truncate(time.Second) + parentID := ids.GenerateTestID() + height := uint64(1337) + + tx := &txs.Tx{ + Unsigned: &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{ + BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{}, + Outs: []*avax.TransferableOutput{}, + }, + }, + StakeOuts: []*avax.TransferableOutput{}, + Validator: validator.Validator{}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{}, + }, + }, + Creds: []verify.Verifiable{}, + } + require.NoError(tx.Sign(txs.Codec, nil)) + + blk, err := NewBanffStandardBlock( + timestamp, + parentID, + height, + []*txs.Tx{tx}, + ) + require.NoError(err) + + // Make sure the block and tx are initialized + require.NotNil(blk.Bytes()) + require.NotNil(blk.Transactions[0].Bytes()) + require.NotEqual(ids.Empty, blk.Transactions[0].ID()) + require.Equal(tx.Bytes(), blk.Transactions[0].Bytes()) + require.Equal(timestamp, blk.Timestamp()) + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} + +func TestNewApricotStandardBlock(t *testing.T) { + require := require.New(t) + + parentID := ids.GenerateTestID() + height := uint64(1337) + + tx := &txs.Tx{ + Unsigned: &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{ + BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{}, + Outs: []*avax.TransferableOutput{}, + }, + }, + StakeOuts: []*avax.TransferableOutput{}, + Validator: validator.Validator{}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{}, + }, + }, + Creds: []verify.Verifiable{}, + } + require.NoError(tx.Sign(txs.Codec, nil)) + + blk, err := NewApricotStandardBlock( + parentID, + height, + []*txs.Tx{tx}, + ) + require.NoError(err) + + // Make sure the block and tx are initialized + require.NotNil(blk.Bytes()) + require.NotNil(blk.Transactions[0].Bytes()) + require.NotEqual(ids.Empty, blk.Transactions[0].ID()) + require.Equal(tx.Bytes(), blk.Transactions[0].Bytes()) + require.Equal(parentID, blk.Parent()) + require.Equal(height, blk.Height()) +} diff --git a/avalanchego/vms/platformvm/blocks/visitor.go b/avalanchego/vms/platformvm/blocks/visitor.go index cd17914f..9cee93b8 100644 --- a/avalanchego/vms/platformvm/blocks/visitor.go +++ b/avalanchego/vms/platformvm/blocks/visitor.go @@ -4,9 +4,14 @@ package blocks type Visitor interface { - AtomicBlock(*AtomicBlock) error - ProposalBlock(*ProposalBlock) error - StandardBlock(*StandardBlock) error - AbortBlock(*AbortBlock) error - CommitBlock(*CommitBlock) error + BanffAbortBlock(*BanffAbortBlock) error + BanffCommitBlock(*BanffCommitBlock) error + BanffProposalBlock(*BanffProposalBlock) error + BanffStandardBlock(*BanffStandardBlock) error + + ApricotAbortBlock(*ApricotAbortBlock) error + ApricotCommitBlock(*ApricotCommitBlock) error + ApricotProposalBlock(*ApricotProposalBlock) error + ApricotStandardBlock(*ApricotStandardBlock) error + ApricotAtomicBlock(*ApricotAtomicBlock) error } diff --git a/avalanchego/vms/platformvm/client.go b/avalanchego/vms/platformvm/client.go index da7dade8..752893cf 100644 --- a/avalanchego/vms/platformvm/client.go +++ b/avalanchego/vms/platformvm/client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm @@ -57,16 +57,16 @@ type Client interface { options ...rpc.Option, ) ([][]byte, ids.ShortID, ids.ID, error) // GetSubnets returns information about the specified subnets - GetSubnets(context.Context, []ids.ID, ...rpc.Option) ([]ClientSubnet, error) + GetSubnets(ctx context.Context, subnetIDs []ids.ID, options ...rpc.Option) ([]ClientSubnet, error) // GetStakingAssetID returns the assetID of the asset used for staking on // subnet corresponding to [subnetID] - GetStakingAssetID(context.Context, ids.ID, ...rpc.Option) (ids.ID, error) + GetStakingAssetID(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (ids.ID, error) // GetCurrentValidators returns the list of current validators for subnet with ID [subnetID] - GetCurrentValidators(ctx context.Context, subnetID ids.ID, nodeIDs []ids.NodeID, options ...rpc.Option) ([]ClientPrimaryValidator, error) + GetCurrentValidators(ctx context.Context, subnetID ids.ID, nodeIDs []ids.NodeID, options ...rpc.Option) ([]ClientPermissionlessValidator, error) // GetPendingValidators returns the list of pending validators for subnet with ID [subnetID] GetPendingValidators(ctx context.Context, subnetID ids.ID, nodeIDs []ids.NodeID, options ...rpc.Option) ([]interface{}, []interface{}, error) // GetCurrentSupply returns an upper bound on the supply of AVAX in the system - GetCurrentSupply(ctx context.Context, options ...rpc.Option) (uint64, error) + GetCurrentSupply(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, error) // SampleValidators returns the nodeIDs of a sample of [sampleSize] validators from the current validator set for subnet with ID [subnetID] SampleValidators(ctx context.Context, subnetID ids.ID, sampleSize uint16, options ...rpc.Option) ([]ids.NodeID, error) // AddValidator issues a transaction to add a validator to the primary network @@ -180,10 +180,10 @@ type Client interface { ) (*GetTxStatusResponse, error) // GetStake returns the amount of nAVAX that [addrs] have cumulatively // staked on the Primary Network. - GetStake(ctx context.Context, addrs []ids.ShortID, options ...rpc.Option) (uint64, [][]byte, error) + GetStake(ctx context.Context, addrs []ids.ShortID, options ...rpc.Option) (map[ids.ID]uint64, [][]byte, error) // GetMinStake returns the minimum staking amount in nAVAX for validators // and delegators respectively - GetMinStake(ctx context.Context, options ...rpc.Option) (uint64, uint64, error) + GetMinStake(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, uint64, error) // GetTotalStake returns the total amount (in nAVAX) staked on the network GetTotalStake(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, error) // GetMaxStakeAmount returns the maximum amount of nAVAX staking to the named @@ -372,7 +372,7 @@ func (c *client) GetCurrentValidators( subnetID ids.ID, nodeIDs []ids.NodeID, options ...rpc.Option, -) ([]ClientPrimaryValidator, error) { +) ([]ClientPermissionlessValidator, error) { res := &GetCurrentValidatorsReply{} err := c.requester.SendRequest(ctx, "getCurrentValidators", &GetCurrentValidatorsArgs{ SubnetID: subnetID, @@ -381,7 +381,7 @@ func (c *client) GetCurrentValidators( if err != nil { return nil, err } - return getClientPrimaryValidators(res.Validators) + return getClientPermissionlessValidators(res.Validators) } func (c *client) GetPendingValidators( @@ -398,9 +398,11 @@ func (c *client) GetPendingValidators( return res.Validators, res.Delegators, err } -func (c *client) GetCurrentSupply(ctx context.Context, options ...rpc.Option) (uint64, error) { +func (c *client) GetCurrentSupply(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, error) { res := &GetCurrentSupplyReply{} - err := c.requester.SendRequest(ctx, "getCurrentSupply", struct{}{}, res, options...) + err := c.requester.SendRequest(ctx, "getCurrentSupply", &GetCurrentSupplyArgs{ + SubnetID: subnetID, + }, res, options...) return uint64(res.Supply), err } @@ -703,7 +705,7 @@ func (c *client) AwaitTxDecided(ctx context.Context, txID ids.ID, freq time.Dura } } -func (c *client) GetStake(ctx context.Context, addrs []ids.ShortID, options ...rpc.Option) (uint64, [][]byte, error) { +func (c *client) GetStake(ctx context.Context, addrs []ids.ShortID, options ...rpc.Option) (map[ids.ID]uint64, [][]byte, error) { res := new(GetStakeReply) err := c.requester.SendRequest(ctx, "getStake", &GetStakeArgs{ JSONAddresses: api.JSONAddresses{ @@ -712,23 +714,30 @@ func (c *client) GetStake(ctx context.Context, addrs []ids.ShortID, options ...r Encoding: formatting.Hex, }, res, options...) if err != nil { - return 0, nil, err + return nil, nil, err + } + + staked := make(map[ids.ID]uint64, len(res.Stakeds)) + for assetID, amount := range res.Stakeds { + staked[assetID] = uint64(amount) } outputs := make([][]byte, len(res.Outputs)) for i, outputStr := range res.Outputs { output, err := formatting.Decode(res.Encoding, outputStr) if err != nil { - return 0, nil, err + return nil, nil, err } outputs[i] = output } - return uint64(res.Staked), outputs, err + return staked, outputs, err } -func (c *client) GetMinStake(ctx context.Context, options ...rpc.Option) (uint64, uint64, error) { +func (c *client) GetMinStake(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, uint64, error) { res := new(GetMinStakeReply) - err := c.requester.SendRequest(ctx, "getMinStake", struct{}{}, res, options...) + err := c.requester.SendRequest(ctx, "getMinStake", &GetMinStakeArgs{ + SubnetID: subnetID, + }, res, options...) return uint64(res.MinValidatorStake), uint64(res.MinDelegatorStake), err } diff --git a/avalanchego/vms/platformvm/client_primary_validator.go b/avalanchego/vms/platformvm/client_permissionless_validator.go similarity index 59% rename from avalanchego/vms/platformvm/client_primary_validator.go rename to avalanchego/vms/platformvm/client_permissionless_validator.go index ced2b3e0..3baa426e 100644 --- a/avalanchego/vms/platformvm/client_primary_validator.go +++ b/avalanchego/vms/platformvm/client_permissionless_validator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm @@ -34,21 +34,22 @@ type ClientOwner struct { Addresses []ids.ShortID } -// ClientPrimaryValidator is the repr. of a primary network validator sent over client -type ClientPrimaryValidator struct { +// ClientPermissionlessValidator is the repr. of a permissionless validator sent +// over client +type ClientPermissionlessValidator struct { ClientStaker - // The owner the staking reward, if applicable, will go to - RewardOwner *ClientOwner - PotentialReward *uint64 - DelegationFee float32 - Uptime *float32 - Connected *bool + ValidationRewardOwner *ClientOwner + DelegationRewardOwner *ClientOwner + PotentialReward *uint64 + DelegationFee float32 + Uptime *float32 + Connected *bool // The delegators delegating to this validator - Delegators []ClientPrimaryDelegator + Delegators []ClientDelegator } -// ClientPrimaryDelegator is the repr. of a primary network delegator sent over client -type ClientPrimaryDelegator struct { +// ClientDelegator is the repr. of a delegator sent over client +type ClientDelegator struct { ClientStaker RewardOwner *ClientOwner PotentialReward *uint64 @@ -78,47 +79,53 @@ func apiOwnerToClientOwner(rewardOwner *api.Owner) (*ClientOwner, error) { }, err } -func getClientPrimaryValidators(validatorsSliceIntf []interface{}) ([]ClientPrimaryValidator, error) { - clientValidators := make([]ClientPrimaryValidator, len(validatorsSliceIntf)) +func getClientPermissionlessValidators(validatorsSliceIntf []interface{}) ([]ClientPermissionlessValidator, error) { + clientValidators := make([]ClientPermissionlessValidator, len(validatorsSliceIntf)) for i, validatorMapIntf := range validatorsSliceIntf { validatorMapJSON, err := json.Marshal(validatorMapIntf) if err != nil { return nil, err } - var apiValidator api.PrimaryValidator + var apiValidator api.PermissionlessValidator err = json.Unmarshal(validatorMapJSON, &apiValidator) if err != nil { return nil, err } - rewardOwner, err := apiOwnerToClientOwner(apiValidator.RewardOwner) + validationRewardOwner, err := apiOwnerToClientOwner(apiValidator.ValidationRewardOwner) + if err != nil { + return nil, err + } + + delegationRewardOwner, err := apiOwnerToClientOwner(apiValidator.DelegationRewardOwner) if err != nil { return nil, err } - clientDelegators := make([]ClientPrimaryDelegator, len(apiValidator.Delegators)) + clientDelegators := make([]ClientDelegator, len(apiValidator.Delegators)) for j, apiDelegator := range apiValidator.Delegators { rewardOwner, err := apiOwnerToClientOwner(apiDelegator.RewardOwner) if err != nil { return nil, err } - clientDelegators[j] = ClientPrimaryDelegator{ + clientDelegators[j] = ClientDelegator{ ClientStaker: apiStakerToClientStaker(apiDelegator.Staker), RewardOwner: rewardOwner, PotentialReward: (*uint64)(apiDelegator.PotentialReward), } } - clientValidators[i] = ClientPrimaryValidator{ - ClientStaker: apiStakerToClientStaker(apiValidator.Staker), - RewardOwner: rewardOwner, - PotentialReward: (*uint64)(apiValidator.PotentialReward), - DelegationFee: float32(apiValidator.DelegationFee), - Uptime: (*float32)(apiValidator.Uptime), - Connected: &apiValidator.Connected, - Delegators: clientDelegators, + clientValidators[i] = ClientPermissionlessValidator{ + ClientStaker: apiStakerToClientStaker(apiValidator.Staker), + ValidationRewardOwner: validationRewardOwner, + DelegationRewardOwner: delegationRewardOwner, + PotentialReward: (*uint64)(apiValidator.PotentialReward), + DelegationFee: float32(apiValidator.DelegationFee), + Uptime: (*float32)(apiValidator.Uptime), + Connected: &apiValidator.Connected, + Delegators: clientDelegators, } } return clientValidators, nil diff --git a/avalanchego/vms/platformvm/config/config.go b/avalanchego/vms/platformvm/config/config.go index 410de399..f86cbf50 100644 --- a/avalanchego/vms/platformvm/config/config.go +++ b/avalanchego/vms/platformvm/config/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package config @@ -36,9 +36,6 @@ type Config struct { // Set of subnets that this node is validating WhitelistedSubnets ids.Set - // Fee that must be burned by every create staker transaction - AddStakerTxFee uint64 - // Fee that is burned by every non-state creating transaction TxFee uint64 @@ -48,9 +45,24 @@ type Config struct { // Fee that must be burned by every subnet creating transaction after AP3 CreateSubnetTxFee uint64 + // Fee that must be burned by every transform subnet transaction + TransformSubnetTxFee uint64 + // Fee that must be burned by every blockchain creating transaction after AP3 CreateBlockchainTxFee uint64 + // Transaction fee for adding a primary network validator + AddPrimaryNetworkValidatorFee uint64 + + // Transaction fee for adding a primary network delegator + AddPrimaryNetworkDelegatorFee uint64 + + // Transaction fee for adding a subnet validator + AddSubnetValidatorFee uint64 + + // Transaction fee for adding a subnet delegator + AddSubnetDelegatorFee uint64 + // The minimum amount of tokens one must bond to be a validator MinValidatorStake uint64 @@ -81,22 +93,34 @@ type Config struct { // Time of the AP5 network upgrade ApricotPhase5Time time.Time - // Time of the Blueberry network upgrade - BlueberryTime time.Time + // Time of the Banff network upgrade + BanffTime time.Time +} + +func (c *Config) IsApricotPhase3Activated(timestamp time.Time) bool { + return !timestamp.Before(c.ApricotPhase3Time) +} + +func (c *Config) IsApricotPhase5Activated(timestamp time.Time) bool { + return !timestamp.Before(c.ApricotPhase5Time) +} + +func (c *Config) IsBanffActivated(timestamp time.Time) bool { + return !timestamp.Before(c.BanffTime) } -func (c *Config) GetCreateBlockchainTxFee(t time.Time) uint64 { - if t.Before(c.ApricotPhase3Time) { - return c.CreateAssetTxFee +func (c *Config) GetCreateBlockchainTxFee(timestamp time.Time) uint64 { + if c.IsApricotPhase3Activated(timestamp) { + return c.CreateBlockchainTxFee } - return c.CreateBlockchainTxFee + return c.CreateAssetTxFee } -func (c *Config) GetCreateSubnetTxFee(t time.Time) uint64 { - if t.Before(c.ApricotPhase3Time) { - return c.CreateAssetTxFee +func (c *Config) GetCreateSubnetTxFee(timestamp time.Time) uint64 { + if c.IsApricotPhase3Activated(timestamp) { + return c.CreateSubnetTxFee } - return c.CreateSubnetTxFee + return c.CreateAssetTxFee } // Create the blockchain described in [tx], but only if this node is a member of diff --git a/avalanchego/vms/platformvm/docs/block_formation_logic.md b/avalanchego/vms/platformvm/docs/block_formation_logic.md new file mode 100644 index 00000000..a9502afc --- /dev/null +++ b/avalanchego/vms/platformvm/docs/block_formation_logic.md @@ -0,0 +1,101 @@ +# Block Composition and Formation Logic + +AvalancheGo v1.9.0 (Banff) slightly changes the way the P-chain selects transactions to be included in next block and deals with block timestamps. In this brief document we detail the process and the changes. + +## Apricot + +### Apricot Block Content + +Apricot allows the following block types with the following content: + +- _Standard Blocks_ may contain multiple transactions of the following types: + - CreateChainTx + - CreateSubnetTx + - ImportTx + - ExportTx +- _Proposal Blocks_ may contain a single transaction of the following types: + - AddValidatorTx + - AddDelegatorTx + - AddSubnetValidatorTx + - RewardValidatorTx + - AdvanceTimeTx +- _Options Blocks_, i.e. _Commit Block_ and _Abort Block_ do not contain any transactions. + +Note that _Atomic Blocks_ were disallowed in the Apricot phase 5 upgrade. They used to contain ImportTx and ExportTx which are now included into Standard Blocks. + +Each block has a header containing: + +- ParentID +- Height + +Note that Apricot block headers do not contain any block timestamp. + +### Apricot Block Formation Logic + +Transactions included in an Apricot block can originate from the mempool or can be created just in time to duly update the staker set. Block formation logic in the Apricot upgrade can be broken up into two high-level steps: + +- First, we try selecting any candidate decision or proposal transactions which could be included in a block _without advancing the current chain time_; +- If no such transactions are found, we evaluate candidate transactions which _may require advancing chain time_. If a chain time advancement is required to include these transactions in a block, a proposal block with an advance time transaction is built first; selected transactions may be included in a subsequent block. + +In more detail, blocks which do not change chain time are built as follows: + +1. If mempool contains any decision transactions, a Standard Block is issued with all of the transactions the default block size can accommodate. Note that Apricot Standard Blocks do not change the current chain time. +2. If the current chain time matches any staker's staking ending time, a reward transaction is issued into a Proposal Block to initiate network voting on whether the specified staker should be rewarded. Note that there could be multiple stakers ending their staking period at the same chain time, hence a Proposal Block must be issued for all of them before the chain time is moved ahead. Any attempt to move chain time ahead before rewarding all stakers would fail block verification. + +While the above steps could be executed in any order, we pick decisions transactions first to maximize throughput. + +Once all possibilities of create a block not advancing chain time are exhausted, we attempt to build a block which _may_ advance chain time as follows: + +1. If the local clock's time is greater than or equal to the earliest change-event timestamp of the staker set, an advance time transaction is issued into a Proposal Block to move current chain time to the earliest change timestamp of the staker set. Upon this Proposal Block's acceptance, chain time will be move ahead and all scheduled changes (e.g. promoting a staker from pending to current) will be carried out. +2. If the mempool contains any proposal transactions, the mempool proposal transaction with the earliest start time is selected and included into a Proposal Block[^1]. A mempool proposal transaction as is won't change the current chain time[^2]. However there is an edge case to consider: on low activity chains (e.g. Fuji P-chain) chain time may fall significantly behind the local clock. If a proposal transaction is finally issued, its start time is likely to be quite far in the future relative to the current chain time. This would cause the proposal transaction to be considered invalid and rejected, since a staker added by a proposal transaction's start time must be at most 366 hours (two weeks) after current chain time. To avoid this edge case on low-activity chains, an advance time transaction is issued first to move chain time to the local clock's time. As soon as chain time is advanced, the mempool proposal transaction will be issued and accepted. + +Note that the order in which these steps are executed matters. A block updating chain time would be deemed invalid if it would advance time beyond the staker set's next change event, skipping the associated changes. The order above ensures this never happens because it checks first if chain time should be moved to the time of the next staker set change. It can also be verified by inspection that the timestamp selected for the advance time transactions always respect the synchrony bound. + +Block formation terminates as soon as any of the steps executed manage to select transactions to be included into a block. Otherwise, an error is raised to signal that there are no transactions to be issued. Finally a timer is kicked off to schedule the next block formation attempt. + +## Banff + +### Banff Block Content + +Banff allows the following block types with the following content: + +- _Standard Blocks_ may contain multiple transactions of the following types: + - CreateChainTx + - CreateSubnetTx + - ImportTx + - ExportTx + - AddValidatorTx + - AddDelegatorTx + - AddSubnetValidatorTx + - RemoveSubnetValidatorTx + - TransformSubnetTx + - AddPermissionlessValidatorTx + - AddPermissionlessDelegatorTx +- _Proposal Blocks_ may contain a single transaction of the following types: + - RewardValidatorTx +- _Options blocks_, i.e. _Commit Block_ and _Abort Block_ do not contain any transactions. + +Note that each block has a header containing: + +- ParentID +- Height +- Time + +So the main differences with respect to Apricot are: + +- _AddValidatorTx_, _AddDelegatorTx_, _AddSubnetValidatorTx_ are included into Standard Blocks rather than Proposal Blocks so that they don't need to be voted on (i.e. followed by a Commit/Abort Block). +- New Transaction types: _RemoveSubnetValidatorTx_, _TransformSubnetTx_, _AddPermissionlessValidatorTx_, _AddPermissionlessDelegatorTx_ have been added into Standard Blocks. +- Block timestamp is explicitly serialized into block header, to allow chain time update. + +### Banff Block Formation Logic + +The activation of the Banff upgrade only makes minor changes to the way the P-chain selects transactions to be included in next block, such as block timestamp calculation. Below are the details of changes. + +Operations are carried out in the following order: + +- We try to move chain time ahead to the current local time or the earliest staker set change event. Unlike Apricot, here we issue either a Standard Block or a Proposal Block to advance the time. +- We check if any staker needs to be rewarded, issuing as many Proposal Blocks as needed, as above. +- We try to fill a Standard Block with mempool decision transactions. + +[^1]: Proposal transactions whose start time is too close to local time are dropped first and won't be included in any block. +[^2]: Advance time transactions are proposal transactions and they do change chain time. But advance time transactions are generated just in time and never stored in the mempool. Here mempool proposal transactions refer to AddValidator, AddDelegator and AddSubnetValidator transactions. Reward validator transactions are proposal transactions which do not change chain time but which never in mempool (they are generated just in time). diff --git a/avalanchego/vms/platformvm/README.md b/avalanchego/vms/platformvm/docs/mempool_gossiping.md similarity index 89% rename from avalanchego/vms/platformvm/README.md rename to avalanchego/vms/platformvm/docs/mempool_gossiping.md index 12f7eede..703dc83b 100644 --- a/avalanchego/vms/platformvm/README.md +++ b/avalanchego/vms/platformvm/docs/mempool_gossiping.md @@ -1,12 +1,10 @@ -# PlatformVM - -## Mempool Gossiping +# Mempool Gossiping The PlatformVM has a mempool which tracks unconfirmed transactions that are waiting to be issued into blocks. The mempool is volatile, i.e. it does not persist unconfirmed transactions. -In conjunction with the introduction of [Snowman++](../proposervm), the mempool was opened to the network, allowing the gossiping of local transactions as well as the hosting of remote ones. +In conjunction with the introduction of [Snowman++](../../proposervm), the mempool was opened to the network, allowing the gossiping of local transactions as well as the hosting of remote ones. -### Mempool Gossiping Workflow +## Mempool Gossiping Workflow The PlatformVM's mempool performs the following workflow: diff --git a/avalanchego/vms/platformvm/docs/subnets.md b/avalanchego/vms/platformvm/docs/subnets.md new file mode 100644 index 00000000..24711e95 --- /dev/null +++ b/avalanchego/vms/platformvm/docs/subnets.md @@ -0,0 +1,36 @@ +# Subnets + +The Avalanche network consists of the Primary Network and a collection of +sub-networks (subnets). + +## Subnet Creation + +Subnets are created by issuing a *CreateSubnetTx*. After a *CreateSubnetTx* is +accepted, a new subnet will exist with the *SubnetID* equal to the *TxID* of the +*CreateSubnetTx*. The *CreateSubnetTx* creates a permissioned subnet. The +*Owner* field in *CreateSubnetTx* specifies who can modify the state of the +subnet. + +## Permissioned Subnets + +A permissioned subnet can be modified by a few different transactions. + +- CreateChainTx + - Creates a new chain that will be validated by all validators of the subnet. +- AddSubnetValidatorTx + - Adds a new validator to the subnet with the specified *StartTime*, + *EndTime*, and *Weight*. +- RemoveSubnetValidatorTx + - Removes a validator from the subnet. +- TransformSubnetTx + - Converts the permissioned subnet into a permissionless subnet. + - Specifies all of the staking parameters. + - AVAX is not allowed to be used as a staking token. In general, it is not + advisable to have multiple subnets using the same staking token. + - After becoming a permissionless subnet, previously added permissioned + validators will remain to finish their staking period. + - No more chains will be able to be added to the subnet. + +### Permissionless Subnets + +Currently, nothing can be performed on a permissionless subnet. diff --git a/avalanchego/vms/platformvm/factory.go b/avalanchego/vms/platformvm/factory.go index 928cddf0..045c93a4 100644 --- a/avalanchego/vms/platformvm/factory.go +++ b/avalanchego/vms/platformvm/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm diff --git a/avalanchego/vms/platformvm/fx/fx.go b/avalanchego/vms/platformvm/fx/fx.go index 9c51211c..d2f15ad0 100644 --- a/avalanchego/vms/platformvm/fx/fx.go +++ b/avalanchego/vms/platformvm/fx/fx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package fx diff --git a/avalanchego/vms/platformvm/genesis/codec.go b/avalanchego/vms/platformvm/genesis/codec.go index aeecec3a..f112ea34 100644 --- a/avalanchego/vms/platformvm/genesis/codec.go +++ b/avalanchego/vms/platformvm/genesis/codec.go @@ -1,33 +1,12 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis import ( - "math" - - "github.com/ava-labs/avalanchego/codec" - "github.com/ava-labs/avalanchego/codec/linearcodec" - "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" ) -var Codec codec.Manager - -func init() { - gc := linearcodec.NewCustomMaxLength(math.MaxInt32) - Codec = codec.NewManager(math.MaxInt32) - - // To maintain codec type ordering, skip positions - // for Proposal/Abort/Commit/Standard/Atomic blocks - gc.SkipRegistrations(5) +const Version = blocks.Version - errs := wrappers.Errs{} - errs.Add( - txs.RegisterUnsignedTxsTypes(gc), - Codec.RegisterCodec(txs.Version, gc), - ) - if errs.Errored() { - panic(errs.Err) - } -} +var Codec = blocks.GenesisCodec diff --git a/avalanchego/vms/platformvm/genesis/genesis.go b/avalanchego/vms/platformvm/genesis/genesis.go index 405b9f04..65cb47dc 100644 --- a/avalanchego/vms/platformvm/genesis/genesis.go +++ b/avalanchego/vms/platformvm/genesis/genesis.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis @@ -30,12 +30,12 @@ func Parse(genesisBytes []byte) (*Genesis, error) { return nil, err } for _, tx := range gen.Validators { - if err := tx.Sign(Codec, nil); err != nil { + if err := tx.Sign(txs.GenesisCodec, nil); err != nil { return nil, err } } for _, tx := range gen.Chains { - if err := tx.Sign(Codec, nil); err != nil { + if err := tx.Sign(txs.GenesisCodec, nil); err != nil { return nil, err } } diff --git a/avalanchego/vms/platformvm/health.go b/avalanchego/vms/platformvm/health.go index 51c417d7..249766a3 100644 --- a/avalanchego/vms/platformvm/health.go +++ b/avalanchego/vms/platformvm/health.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm diff --git a/avalanchego/vms/platformvm/message/codec.go b/avalanchego/vms/platformvm/message/codec.go index bd389704..f0c69d6e 100644 --- a/avalanchego/vms/platformvm/message/codec.go +++ b/avalanchego/vms/platformvm/message/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message diff --git a/avalanchego/vms/platformvm/message/handler.go b/avalanchego/vms/platformvm/message/handler.go index d8b3b944..925e4682 100644 --- a/avalanchego/vms/platformvm/message/handler.go +++ b/avalanchego/vms/platformvm/message/handler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message diff --git a/avalanchego/vms/platformvm/message/handler_test.go b/avalanchego/vms/platformvm/message/handler_test.go index a738ba4e..1407d2ce 100644 --- a/avalanchego/vms/platformvm/message/handler_test.go +++ b/avalanchego/vms/platformvm/message/handler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message diff --git a/avalanchego/vms/platformvm/message/message.go b/avalanchego/vms/platformvm/message/message.go index afe34467..121a61d5 100644 --- a/avalanchego/vms/platformvm/message/message.go +++ b/avalanchego/vms/platformvm/message/message.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message diff --git a/avalanchego/vms/platformvm/message/message_test.go b/avalanchego/vms/platformvm/message/message_test.go index 5faaa2d0..1ffe71b4 100644 --- a/avalanchego/vms/platformvm/message/message_test.go +++ b/avalanchego/vms/platformvm/message/message_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package message diff --git a/avalanchego/vms/platformvm/metrics/block_metrics.go b/avalanchego/vms/platformvm/metrics/block_metrics.go index 59389744..1a10267b 100644 --- a/avalanchego/vms/platformvm/metrics/block_metrics.go +++ b/avalanchego/vms/platformvm/metrics/block_metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics @@ -56,17 +56,27 @@ func newBlockMetric( return blockMetric } -func (m *blockMetrics) ProposalBlock(b *blocks.ProposalBlock) error { - m.numProposalBlocks.Inc() - return b.Tx.Unsigned.Visit(m.txMetrics) +func (m *blockMetrics) BanffAbortBlock(*blocks.BanffAbortBlock) error { + m.numAbortBlocks.Inc() + return nil } -func (m *blockMetrics) AtomicBlock(b *blocks.AtomicBlock) error { - m.numAtomicBlocks.Inc() +func (m *blockMetrics) BanffCommitBlock(*blocks.BanffCommitBlock) error { + m.numCommitBlocks.Inc() + return nil +} + +func (m *blockMetrics) BanffProposalBlock(b *blocks.BanffProposalBlock) error { + m.numProposalBlocks.Inc() + for _, tx := range b.Transactions { + if err := tx.Unsigned.Visit(m.txMetrics); err != nil { + return err + } + } return b.Tx.Unsigned.Visit(m.txMetrics) } -func (m *blockMetrics) StandardBlock(b *blocks.StandardBlock) error { +func (m *blockMetrics) BanffStandardBlock(b *blocks.BanffStandardBlock) error { m.numStandardBlocks.Inc() for _, tx := range b.Transactions { if err := tx.Unsigned.Visit(m.txMetrics); err != nil { @@ -76,12 +86,32 @@ func (m *blockMetrics) StandardBlock(b *blocks.StandardBlock) error { return nil } -func (m *blockMetrics) CommitBlock(*blocks.CommitBlock) error { +func (m *blockMetrics) ApricotAbortBlock(*blocks.ApricotAbortBlock) error { + m.numAbortBlocks.Inc() + return nil +} + +func (m *blockMetrics) ApricotCommitBlock(*blocks.ApricotCommitBlock) error { m.numCommitBlocks.Inc() return nil } -func (m *blockMetrics) AbortBlock(*blocks.AbortBlock) error { - m.numAbortBlocks.Inc() +func (m *blockMetrics) ApricotProposalBlock(b *blocks.ApricotProposalBlock) error { + m.numProposalBlocks.Inc() + return b.Tx.Unsigned.Visit(m.txMetrics) +} + +func (m *blockMetrics) ApricotStandardBlock(b *blocks.ApricotStandardBlock) error { + m.numStandardBlocks.Inc() + for _, tx := range b.Transactions { + if err := tx.Unsigned.Visit(m.txMetrics); err != nil { + return err + } + } return nil } + +func (m *blockMetrics) ApricotAtomicBlock(b *blocks.ApricotAtomicBlock) error { + m.numAtomicBlocks.Inc() + return b.Tx.Unsigned.Visit(m.txMetrics) +} diff --git a/avalanchego/vms/platformvm/metrics/metrics.go b/avalanchego/vms/platformvm/metrics/metrics.go index 2eb0ad78..18cc253d 100644 --- a/avalanchego/vms/platformvm/metrics/metrics.go +++ b/avalanchego/vms/platformvm/metrics/metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/vms/platformvm/metrics/no_op.go b/avalanchego/vms/platformvm/metrics/no_op.go index ef067f76..d3fb9923 100644 --- a/avalanchego/vms/platformvm/metrics/no_op.go +++ b/avalanchego/vms/platformvm/metrics/no_op.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics diff --git a/avalanchego/vms/platformvm/metrics/tx_metrics.go b/avalanchego/vms/platformvm/metrics/tx_metrics.go index 380d2b08..a47757e8 100644 --- a/avalanchego/vms/platformvm/metrics/tx_metrics.go +++ b/avalanchego/vms/platformvm/metrics/tx_metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package metrics @@ -24,8 +24,10 @@ type txMetrics struct { numExportTxs, numImportTxs, numRewardValidatorTxs, + numRemoveSubnetValidatorTxs, numTransformSubnetTxs, - numAddGeneralValidatorTxs prometheus.Counter + numAddPermissionlessValidatorTxs, + numAddPermissionlessDelegatorTxs prometheus.Counter } func newTxMetrics( @@ -34,17 +36,19 @@ func newTxMetrics( ) (*txMetrics, error) { errs := wrappers.Errs{} m := &txMetrics{ - numAddDelegatorTxs: newTxMetric(namespace, "add_delegator", registerer, &errs), - numAddSubnetValidatorTxs: newTxMetric(namespace, "add_subnet_validator", registerer, &errs), - numAddValidatorTxs: newTxMetric(namespace, "add_validator", registerer, &errs), - numAdvanceTimeTxs: newTxMetric(namespace, "advance_time", registerer, &errs), - numCreateChainTxs: newTxMetric(namespace, "create_chain", registerer, &errs), - numCreateSubnetTxs: newTxMetric(namespace, "create_subnet", registerer, &errs), - numExportTxs: newTxMetric(namespace, "export", registerer, &errs), - numImportTxs: newTxMetric(namespace, "import", registerer, &errs), - numRewardValidatorTxs: newTxMetric(namespace, "reward_validator", registerer, &errs), - numTransformSubnetTxs: newTxMetric(namespace, "transform_subnet", registerer, &errs), - numAddGeneralValidatorTxs: newTxMetric(namespace, "add_general_validator", registerer, &errs), + numAddDelegatorTxs: newTxMetric(namespace, "add_delegator", registerer, &errs), + numAddSubnetValidatorTxs: newTxMetric(namespace, "add_subnet_validator", registerer, &errs), + numAddValidatorTxs: newTxMetric(namespace, "add_validator", registerer, &errs), + numAdvanceTimeTxs: newTxMetric(namespace, "advance_time", registerer, &errs), + numCreateChainTxs: newTxMetric(namespace, "create_chain", registerer, &errs), + numCreateSubnetTxs: newTxMetric(namespace, "create_subnet", registerer, &errs), + numExportTxs: newTxMetric(namespace, "export", registerer, &errs), + numImportTxs: newTxMetric(namespace, "import", registerer, &errs), + numRewardValidatorTxs: newTxMetric(namespace, "reward_validator", registerer, &errs), + numRemoveSubnetValidatorTxs: newTxMetric(namespace, "remove_subnet_validator", registerer, &errs), + numTransformSubnetTxs: newTxMetric(namespace, "transform_subnet", registerer, &errs), + numAddPermissionlessValidatorTxs: newTxMetric(namespace, "add_permissionless_validator", registerer, &errs), + numAddPermissionlessDelegatorTxs: newTxMetric(namespace, "add_permissionless_delegator", registerer, &errs), } return m, errs.Err } @@ -108,3 +112,23 @@ func (m *txMetrics) RewardValidatorTx(*txs.RewardValidatorTx) error { m.numRewardValidatorTxs.Inc() return nil } + +func (m *txMetrics) RemoveSubnetValidatorTx(*txs.RemoveSubnetValidatorTx) error { + m.numRemoveSubnetValidatorTxs.Inc() + return nil +} + +func (m *txMetrics) TransformSubnetTx(*txs.TransformSubnetTx) error { + m.numTransformSubnetTxs.Inc() + return nil +} + +func (m *txMetrics) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error { + m.numAddPermissionlessValidatorTxs.Inc() + return nil +} + +func (m *txMetrics) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegatorTx) error { + m.numAddPermissionlessDelegatorTxs.Inc() + return nil +} diff --git a/avalanchego/vms/platformvm/reward/calculator.go b/avalanchego/vms/platformvm/reward/calculator.go index 9ac799c1..8669ab90 100644 --- a/avalanchego/vms/platformvm/reward/calculator.go +++ b/avalanchego/vms/platformvm/reward/calculator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package reward diff --git a/avalanchego/vms/platformvm/reward/calculator_test.go b/avalanchego/vms/platformvm/reward/calculator_test.go index 9ae01b21..4bdb716a 100644 --- a/avalanchego/vms/platformvm/reward/calculator_test.go +++ b/avalanchego/vms/platformvm/reward/calculator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package reward @@ -134,3 +134,45 @@ func TestRewards(t *testing.T) { }) } } + +// func TestRewardsOverflow(t *testing.T) { +// require := require.New(t) + +// var ( +// maxSupply uint64 = math.MaxUint64 +// initialSupply uint64 = 1 +// ) +// c := NewCalculator(Config{ +// MaxConsumptionRate: PercentDenominator, +// MinConsumptionRate: PercentDenominator, +// MintingPeriod: defaultMinStakingDuration, +// SupplyCap: maxSupply, +// }) +// rewards := c.Calculate( +// defaultMinStakingDuration, +// maxSupply, // The staked amount is larger than the current supply +// initialSupply, +// ) +// require.Equal(maxSupply-initialSupply, rewards) +// } + +// func TestRewardsMint(t *testing.T) { +// require := require.New(t) + +// var ( +// maxSupply uint64 = 1000 +// initialSupply uint64 = 1 +// ) +// c := NewCalculator(Config{ +// MaxConsumptionRate: PercentDenominator, +// MinConsumptionRate: PercentDenominator, +// MintingPeriod: defaultMinStakingDuration, +// SupplyCap: maxSupply, +// }) +// rewards := c.Calculate( +// defaultMinStakingDuration, +// maxSupply, // The staked amount is larger than the current supply +// initialSupply, +// ) +// require.Equal(maxSupply-initialSupply, rewards) +// } diff --git a/avalanchego/vms/platformvm/reward/config.go b/avalanchego/vms/platformvm/reward/config.go index f3890e08..cf4fa8e3 100644 --- a/avalanchego/vms/platformvm/reward/config.go +++ b/avalanchego/vms/platformvm/reward/config.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package reward diff --git a/avalanchego/vms/platformvm/service.go b/avalanchego/vms/platformvm/service.go index d09bb822..8d2b3f01 100644 --- a/avalanchego/vms/platformvm/service.go +++ b/avalanchego/vms/platformvm/service.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm @@ -9,6 +9,8 @@ import ( "net/http" "time" + stdmath "math" + "go.uber.org/zap" "github.com/ava-labs/avalanchego/api" @@ -23,7 +25,6 @@ import ( "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/keystore" - "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/reward" "github.com/ava-labs/avalanchego/vms/platformvm/stakeable" "github.com/ava-labs/avalanchego/vms/platformvm/status" @@ -48,30 +49,24 @@ const ( ) var ( - errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks") - errNoSubnetID = errors.New("argument 'subnetID' not provided") - errNoRewardAddress = errors.New("argument 'rewardAddress' not provided") - errInvalidDelegationRate = errors.New("argument 'delegationFeeRate' must be between 0 and 100, inclusive") - errNoAddresses = errors.New("no addresses provided") - errNoKeys = errors.New("user has no keys or funds") - errNoPrimaryValidators = errors.New("no default subnet validators") - errNoValidators = errors.New("no subnet validators") - errStartTimeTooSoon = fmt.Errorf("start time must be at least %s in the future", minAddStakerDelay) - errStartTimeTooLate = errors.New("start time is too far in the future") - errTotalOverflow = errors.New("overflow while calculating total balance") - errUnlockedOverflow = errors.New("overflow while calculating unlocked balance") - errLockedOverflow = errors.New("overflow while calculating locked balance") - errNotStakeableOverflow = errors.New("overflow while calculating locked not stakeable balance") - errLockedNotStakeableOverflow = errors.New("overflow while calculating locked not stakeable balance") - errUnlockedStakeableOverflow = errors.New("overflow while calculating unlocked stakeable balance") - errNamedSubnetCantBePrimary = errors.New("subnet validator attempts to validate primary network") - errNoAmount = errors.New("argument 'amount' must be > 0") - errMissingName = errors.New("argument 'name' not given") - errMissingVMID = errors.New("argument 'vmID' not given") - errMissingBlockchainID = errors.New("argument 'blockchainID' not given") - errMissingPrivateKey = errors.New("argument 'privateKey' not given") - errStartAfterEndTime = errors.New("start time must be before end time") - errStartTimeInThePast = errors.New("start time in the past") + errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks") + errNoSubnetID = errors.New("argument 'subnetID' not provided") + errNoRewardAddress = errors.New("argument 'rewardAddress' not provided") + errInvalidDelegationRate = errors.New("argument 'delegationFeeRate' must be between 0 and 100, inclusive") + errNoAddresses = errors.New("no addresses provided") + errNoKeys = errors.New("user has no keys or funds") + errNoPrimaryValidators = errors.New("no default subnet validators") + errNoValidators = errors.New("no subnet validators") + errStartTimeTooSoon = fmt.Errorf("start time must be at least %s in the future", minAddStakerDelay) + errStartTimeTooLate = errors.New("start time is too far in the future") + errNamedSubnetCantBePrimary = errors.New("subnet validator attempts to validate primary network") + errNoAmount = errors.New("argument 'amount' must be > 0") + errMissingName = errors.New("argument 'name' not given") + errMissingVMID = errors.New("argument 'vmID' not given") + errMissingBlockchainID = errors.New("argument 'blockchainID' not given") + errMissingPrivateKey = errors.New("argument 'privateKey' not given") + errStartAfterEndTime = errors.New("start time must be before end time") + errStartTimeInThePast = errors.New("start time in the past") ) // Service defines the API calls that can be made to the platform chain @@ -180,13 +175,19 @@ type GetBalanceRequest struct { Addresses []string `json:"addresses"` } +// Note: We explicitly duplicate AVAX out of the maps to ensure backwards +// compatibility. type GetBalanceResponse struct { // Balance, in nAVAX, of the address - Balance json.Uint64 `json:"balance"` - Unlocked json.Uint64 `json:"unlocked"` - LockedStakeable json.Uint64 `json:"lockedStakeable"` - LockedNotStakeable json.Uint64 `json:"lockedNotStakeable"` - UTXOIDs []*avax.UTXOID `json:"utxoIDs"` + Balance json.Uint64 `json:"balance"` + Unlocked json.Uint64 `json:"unlocked"` + LockedStakeable json.Uint64 `json:"lockedStakeable"` + LockedNotStakeable json.Uint64 `json:"lockedNotStakeable"` + Balances map[ids.ID]json.Uint64 `json:"balances"` + Unlockeds map[ids.ID]json.Uint64 `json:"unlockeds"` + LockedStakeables map[ids.ID]json.Uint64 `json:"lockedStakeables"` + LockedNotStakeables map[ids.ID]json.Uint64 `json:"lockedNotStakeables"` + UTXOIDs []*avax.UTXOID `json:"utxoIDs"` } // GetBalance gets the balance of an address @@ -212,26 +213,29 @@ func (service *Service) GetBalance(_ *http.Request, args *GetBalanceRequest, res currentTime := service.vm.clock.Unix() - unlocked := uint64(0) - lockedStakeable := uint64(0) - lockedNotStakeable := uint64(0) + unlockeds := map[ids.ID]uint64{} + lockedStakeables := map[ids.ID]uint64{} + lockedNotStakeables := map[ids.ID]uint64{} utxoFor: for _, utxo := range utxos { + assetID := utxo.AssetID() switch out := utxo.Out.(type) { case *secp256k1fx.TransferOutput: if out.Locktime <= currentTime { - newBalance, err := math.Add64(unlocked, out.Amount()) + newBalance, err := math.Add64(unlockeds[assetID], out.Amount()) if err != nil { - return errUnlockedOverflow + unlockeds[assetID] = stdmath.MaxUint64 + } else { + unlockeds[assetID] = newBalance } - unlocked = newBalance } else { - newBalance, err := math.Add64(lockedNotStakeable, out.Amount()) + newBalance, err := math.Add64(lockedNotStakeables[assetID], out.Amount()) if err != nil { - return errNotStakeableOverflow + lockedNotStakeables[assetID] = stdmath.MaxUint64 + } else { + lockedNotStakeables[assetID] = newBalance } - lockedNotStakeable = newBalance } case *stakeable.LockOut: innerOut, ok := out.TransferableOut.(*secp256k1fx.TransferOutput) @@ -242,23 +246,26 @@ utxoFor: ) continue utxoFor case innerOut.Locktime > currentTime: - newBalance, err := math.Add64(lockedNotStakeable, out.Amount()) + newBalance, err := math.Add64(lockedNotStakeables[assetID], out.Amount()) if err != nil { - return errLockedNotStakeableOverflow + lockedNotStakeables[assetID] = stdmath.MaxUint64 + } else { + lockedNotStakeables[assetID] = newBalance } - lockedNotStakeable = newBalance case out.Locktime <= currentTime: - newBalance, err := math.Add64(unlocked, out.Amount()) + newBalance, err := math.Add64(unlockeds[assetID], out.Amount()) if err != nil { - return errUnlockedOverflow + unlockeds[assetID] = stdmath.MaxUint64 + } else { + unlockeds[assetID] = newBalance } - unlocked = newBalance default: - newBalance, err := math.Add64(lockedStakeable, out.Amount()) + newBalance, err := math.Add64(lockedStakeables[assetID], out.Amount()) if err != nil { - return errUnlockedStakeableOverflow + lockedStakeables[assetID] = stdmath.MaxUint64 + } else { + lockedStakeables[assetID] = newBalance } - lockedStakeable = newBalance } default: continue utxoFor @@ -267,22 +274,46 @@ utxoFor: response.UTXOIDs = append(response.UTXOIDs, &utxo.UTXOID) } - lockedBalance, err := math.Add64(lockedStakeable, lockedNotStakeable) - if err != nil { - return errLockedOverflow + balances := map[ids.ID]uint64{} + for assetID, amount := range lockedStakeables { + balances[assetID] = amount } - balance, err := math.Add64(unlocked, lockedBalance) - if err != nil { - return errTotalOverflow + for assetID, amount := range lockedNotStakeables { + newBalance, err := math.Add64(balances[assetID], amount) + if err != nil { + balances[assetID] = stdmath.MaxUint64 + } else { + balances[assetID] = newBalance + } + } + for assetID, amount := range unlockeds { + newBalance, err := math.Add64(balances[assetID], amount) + if err != nil { + balances[assetID] = stdmath.MaxUint64 + } else { + balances[assetID] = newBalance + } } - response.Balance = json.Uint64(balance) - response.Unlocked = json.Uint64(unlocked) - response.LockedStakeable = json.Uint64(lockedStakeable) - response.LockedNotStakeable = json.Uint64(lockedNotStakeable) + response.Balances = newJSONBalanceMap(balances) + response.Unlockeds = newJSONBalanceMap(unlockeds) + response.LockedStakeables = newJSONBalanceMap(lockedStakeables) + response.LockedNotStakeables = newJSONBalanceMap(lockedNotStakeables) + response.Balance = response.Balances[service.vm.ctx.AVAXAssetID] + response.Unlocked = response.Unlockeds[service.vm.ctx.AVAXAssetID] + response.LockedStakeable = response.LockedStakeables[service.vm.ctx.AVAXAssetID] + response.LockedNotStakeable = response.LockedNotStakeables[service.vm.ctx.AVAXAssetID] return nil } +func newJSONBalanceMap(balanceMap map[ids.ID]uint64) map[ids.ID]json.Uint64 { + jsonBalanceMap := make(map[ids.ID]json.Uint64, len(balanceMap)) + for assetID, amount := range balanceMap { + jsonBalanceMap[assetID] = json.Uint64(amount) + } + return jsonBalanceMap +} + // CreateAddress creates an address controlled by [args.Username] // Returns the newly created address func (service *Service) CreateAddress(_ *http.Request, args *api.UserPass, response *api.JSONAddress) error { @@ -477,6 +508,16 @@ func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, respon response.Subnets = make([]APISubnet, len(subnets)+1) for i, subnet := range subnets { + subnetID := subnet.ID() + if _, err := service.vm.state.GetSubnetTransformation(subnetID); err == nil { + response.Subnets[i] = APISubnet{ + ID: subnetID, + ControlKeys: []string{}, + Threshold: json.Uint32(0), + } + continue + } + unsignedTx := subnet.Unsigned.(*txs.CreateSubnetTx) owner := unsignedTx.Owner.(*secp256k1fx.OutputOwners) controlAddrs := []string{} @@ -488,7 +529,7 @@ func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, respon controlAddrs = append(controlAddrs, addr) } response.Subnets[i] = APISubnet{ - ID: subnet.ID(), + ID: subnetID, ControlKeys: controlAddrs, Threshold: json.Uint32(owner.Threshold), } @@ -520,6 +561,15 @@ func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, respon continue } + if _, err := service.vm.state.GetSubnetTransformation(subnetID); err == nil { + response.Subnets = append(response.Subnets, APISubnet{ + ID: subnetID, + ControlKeys: []string{}, + Threshold: json.Uint32(0), + }) + continue + } + subnetTx, _, err := service.vm.state.GetTx(subnetID) if err == database.ErrNotFound { continue @@ -546,13 +596,11 @@ func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, respon controlAddrs[i] = addr } - response.Subnets = append(response.Subnets, - APISubnet{ - ID: subnetID, - ControlKeys: controlAddrs, - Threshold: json.Uint32(owner.Threshold), - }, - ) + response.Subnets = append(response.Subnets, APISubnet{ + ID: subnetID, + ControlKeys: controlAddrs, + Threshold: json.Uint32(owner.Threshold), + }) } return nil } @@ -572,11 +620,28 @@ type GetStakingAssetIDResponse struct { func (service *Service) GetStakingAssetID(_ *http.Request, args *GetStakingAssetIDArgs, response *GetStakingAssetIDResponse) error { service.vm.ctx.Log.Debug("Platform: GetStakingAssetID called") - if args.SubnetID != constants.PrimaryNetworkID { - return fmt.Errorf("subnet %s doesn't have a valid staking token", args.SubnetID) + if args.SubnetID == constants.PrimaryNetworkID { + response.AssetID = service.vm.ctx.AVAXAssetID + return nil + } + + transformSubnetIntf, err := service.vm.state.GetSubnetTransformation(args.SubnetID) + if err != nil { + return fmt.Errorf( + "failed fetching subnet transformation for %s: %w", + args.SubnetID, + err, + ) + } + transformSubnet, ok := transformSubnetIntf.Unsigned.(*txs.TransformSubnetTx) + if !ok { + return fmt.Errorf( + "unexpected subnet transformation tx type fetched %T", + transformSubnetIntf.Unsigned, + ) } - response.AssetID = service.vm.ctx.AVAXAssetID + response.AssetID = transformSubnet.AssetID return nil } @@ -647,47 +712,77 @@ func (service *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentVa potentialReward := json.Uint64(staker.PotentialReward) switch staker := tx.Unsigned.(type) { - case *txs.AddDelegatorTx: - var rewardOwner *platformapi.Owner - owner, ok := staker.RewardsOwner.(*secp256k1fx.OutputOwners) + case txs.ValidatorTx: + shares := staker.Shares() + delegationFee := json.Float32(100 * float32(shares) / float32(reward.PercentDenominator)) + + primaryNetworkStaker, err := service.vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + if err != nil { + return err + } + + // TODO: calculate subnet uptimes + rawUptime, err := service.vm.uptimeManager.CalculateUptimePercentFrom(nodeID, primaryNetworkStaker.StartTime) + if err != nil { + return err + } + uptime := json.Float32(rawUptime) + + connected := service.vm.uptimeManager.IsConnected(nodeID) + tracksSubnet := args.SubnetID == constants.PrimaryNetworkID || service.vm.SubnetTracker.TracksSubnet(nodeID, args.SubnetID) + + var ( + validationRewardOwner *platformapi.Owner + delegationRewardOwner *platformapi.Owner + ) + validationOwner, ok := staker.ValidationRewardsOwner().(*secp256k1fx.OutputOwners) if ok { - rewardOwner = &platformapi.Owner{ - Locktime: json.Uint64(owner.Locktime), - Threshold: json.Uint32(owner.Threshold), + validationRewardOwner = &platformapi.Owner{ + Locktime: json.Uint64(validationOwner.Locktime), + Threshold: json.Uint32(validationOwner.Threshold), } - for _, addr := range owner.Addrs { + for _, addr := range validationOwner.Addrs { addrStr, err := service.addrManager.FormatLocalAddress(addr) if err != nil { return err } - rewardOwner.Addresses = append(rewardOwner.Addresses, addrStr) + validationRewardOwner.Addresses = append(validationRewardOwner.Addresses, addrStr) + } + } + delegationOwner, ok := staker.DelegationRewardsOwner().(*secp256k1fx.OutputOwners) + if ok { + delegationRewardOwner = &platformapi.Owner{ + Locktime: json.Uint64(delegationOwner.Locktime), + Threshold: json.Uint32(delegationOwner.Threshold), + } + for _, addr := range delegationOwner.Addrs { + addrStr, err := service.addrManager.FormatLocalAddress(addr) + if err != nil { + return err + } + delegationRewardOwner.Addresses = append(delegationRewardOwner.Addresses, addrStr) } } - delegator := platformapi.PrimaryDelegator{ + reply.Validators = append(reply.Validators, platformapi.PermissionlessValidator{ Staker: platformapi.Staker{ TxID: txID, + NodeID: nodeID, StartTime: startTime, EndTime: endTime, StakeAmount: &weight, - NodeID: nodeID, }, - RewardOwner: rewardOwner, - PotentialReward: &potentialReward, - } - vdrToDelegators[delegator.NodeID] = append(vdrToDelegators[delegator.NodeID], delegator) - case *txs.AddValidatorTx: - delegationFee := json.Float32(100 * float32(staker.Shares) / float32(reward.PercentDenominator)) - rawUptime, err := service.vm.uptimeManager.CalculateUptimePercentFrom(nodeID, staker.StartTime()) - if err != nil { - return err - } - uptime := json.Float32(rawUptime) - - connected := service.vm.uptimeManager.IsConnected(nodeID) - + Uptime: &uptime, + Connected: connected && tracksSubnet, + PotentialReward: &potentialReward, + RewardOwner: validationRewardOwner, + ValidationRewardOwner: validationRewardOwner, + DelegationRewardOwner: delegationRewardOwner, + DelegationFee: delegationFee, + }) + case txs.DelegatorTx: var rewardOwner *platformapi.Owner - owner, ok := staker.RewardsOwner.(*secp256k1fx.OutputOwners) + owner, ok := staker.RewardsOwner().(*secp256k1fx.OutputOwners) if ok { rewardOwner = &platformapi.Owner{ Locktime: json.Uint64(owner.Locktime), @@ -702,24 +797,22 @@ func (service *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentVa } } - reply.Validators = append(reply.Validators, platformapi.PrimaryValidator{ + delegator := platformapi.PrimaryDelegator{ Staker: platformapi.Staker{ TxID: txID, - NodeID: nodeID, StartTime: startTime, EndTime: endTime, StakeAmount: &weight, + NodeID: nodeID, }, - Uptime: &uptime, - Connected: connected, - PotentialReward: &potentialReward, RewardOwner: rewardOwner, - DelegationFee: delegationFee, - }) + PotentialReward: &potentialReward, + } + vdrToDelegators[delegator.NodeID] = append(vdrToDelegators[delegator.NodeID], delegator) case *txs.AddSubnetValidatorTx: connected := service.vm.uptimeManager.IsConnected(nodeID) tracksSubnet := service.vm.SubnetTracker.TracksSubnet(nodeID, args.SubnetID) - reply.Validators = append(reply.Validators, platformapi.SubnetValidator{ + reply.Validators = append(reply.Validators, platformapi.PermissionedValidator{ Staker: platformapi.Staker{ NodeID: nodeID, TxID: txID, @@ -735,7 +828,7 @@ func (service *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentVa } for i, vdrIntf := range reply.Validators { - vdr, ok := vdrIntf.(platformapi.PrimaryValidator) + vdr, ok := vdrIntf.(platformapi.PermissionlessValidator) if !ok { continue } @@ -804,19 +897,13 @@ func (service *Service) GetPendingValidators(_ *http.Request, args *GetPendingVa endTime := json.Uint64(staker.EndTime.Unix()) switch staker := tx.Unsigned.(type) { - case *txs.AddDelegatorTx: - reply.Delegators = append(reply.Delegators, platformapi.Staker{ - TxID: txID, - NodeID: nodeID, - StartTime: startTime, - EndTime: endTime, - StakeAmount: &weight, - }) - case *txs.AddValidatorTx: - delegationFee := json.Float32(100 * float32(staker.Shares) / float32(reward.PercentDenominator)) + case txs.ValidatorTx: + shares := staker.Shares() + delegationFee := json.Float32(100 * float32(shares) / float32(reward.PercentDenominator)) connected := service.vm.uptimeManager.IsConnected(nodeID) - reply.Validators = append(reply.Validators, platformapi.PrimaryValidator{ + tracksSubnet := args.SubnetID == constants.PrimaryNetworkID || service.vm.SubnetTracker.TracksSubnet(nodeID, args.SubnetID) + reply.Validators = append(reply.Validators, platformapi.PermissionlessValidator{ Staker: platformapi.Staker{ TxID: txID, NodeID: nodeID, @@ -825,12 +912,22 @@ func (service *Service) GetPendingValidators(_ *http.Request, args *GetPendingVa StakeAmount: &weight, }, DelegationFee: delegationFee, - Connected: connected, + Connected: connected && tracksSubnet, }) + + case txs.DelegatorTx: + reply.Delegators = append(reply.Delegators, platformapi.Staker{ + TxID: txID, + NodeID: nodeID, + StartTime: startTime, + EndTime: endTime, + StakeAmount: &weight, + }) + case *txs.AddSubnetValidatorTx: connected := service.vm.uptimeManager.IsConnected(nodeID) tracksSubnet := service.vm.SubnetTracker.TracksSubnet(nodeID, args.SubnetID) - reply.Validators = append(reply.Validators, platformapi.SubnetValidator{ + reply.Validators = append(reply.Validators, platformapi.PermissionedValidator{ Staker: platformapi.Staker{ NodeID: nodeID, TxID: txID, @@ -847,17 +944,23 @@ func (service *Service) GetPendingValidators(_ *http.Request, args *GetPendingVa return nil } +// GetCurrentSupplyArgs are the arguments for calling GetCurrentSupply +type GetCurrentSupplyArgs struct { + SubnetID ids.ID `json:"subnetID"` +} + // GetCurrentSupplyReply are the results from calling GetCurrentSupply type GetCurrentSupplyReply struct { Supply json.Uint64 `json:"supply"` } // GetCurrentSupply returns an upper bound on the supply of AVAX in the system -func (service *Service) GetCurrentSupply(_ *http.Request, _ *struct{}, reply *GetCurrentSupplyReply) error { +func (service *Service) GetCurrentSupply(_ *http.Request, args *GetCurrentSupplyArgs, reply *GetCurrentSupplyReply) error { service.vm.ctx.Log.Debug("Platform: GetCurrentSupply called") - reply.Supply = json.Uint64(service.vm.state.GetCurrentSupply()) - return nil + supply, err := service.vm.state.GetCurrentSupply(args.SubnetID) + reply.Supply = json.Uint64(supply) + return err } // SampleValidatorsArgs are the arguments for calling SampleValidators @@ -1971,7 +2074,8 @@ type GetStakeArgs struct { // GetStakeReply is the response from calling GetStake. type GetStakeReply struct { - Staked json.Uint64 `json:"staked"` + Staked json.Uint64 `json:"staked"` + Stakeds map[ids.ID]json.Uint64 `json:"stakeds"` // String representation of staked outputs // Each is of type avax.TransferableOutput Outputs []string `json:"stakedOutputs"` @@ -1983,35 +2087,17 @@ type GetStakeReply struct { // Returns: // 1) The total amount staked by addresses in [addrs] // 2) The staked outputs -func (service *Service) getStakeHelper(tx *txs.Tx, addrs ids.ShortSet) (uint64, []avax.TransferableOutput, error) { - var outs []*avax.TransferableOutput - switch staker := tx.Unsigned.(type) { - case *txs.AddDelegatorTx: - outs = staker.Stake - case *txs.AddValidatorTx: - outs = staker.Stake - case *txs.AddSubnetValidatorTx: - return 0, nil, nil - default: - err := fmt.Errorf("expected *txs.AddDelegatorTx, *txs.AddValidatorTx or *txs.AddSubnetValidatorTx but got %T", tx.Unsigned) - service.vm.ctx.Log.Error("invalid tx type provided from validator set", - zap.Error(err), - ) - return 0, nil, err +func (service *Service) getStakeHelper(tx *txs.Tx, addrs ids.ShortSet, totalAmountStaked map[ids.ID]uint64) []avax.TransferableOutput { + staker, ok := tx.Unsigned.(txs.PermissionlessStaker) + if !ok { + return nil } - var ( - totalAmountStaked uint64 - err error - stakedOuts = make([]avax.TransferableOutput, 0, len(outs)) - ) + stake := staker.Stake() + stakedOuts := make([]avax.TransferableOutput, 0, len(stake)) // Go through all of the staked outputs - for _, stake := range outs { - // This output isn't AVAX. Ignore. - if stake.AssetID() != service.vm.ctx.AVAXAssetID { - continue - } - out := stake.Out + for _, output := range stake { + out := output.Out if lockedOut, ok := out.(*stakeable.LockOut); ok { // This output can only be used for staking until [stakeOnlyUntil] out = lockedOut.TransferableOut @@ -2020,6 +2106,7 @@ func (service *Service) getStakeHelper(tx *txs.Tx, addrs ids.ShortSet) (uint64, if !ok { continue } + // Check whether this output is owned by one of the given addresses contains := false for _, addr := range secpOut.Addrs { @@ -2032,16 +2119,20 @@ func (service *Service) getStakeHelper(tx *txs.Tx, addrs ids.ShortSet) (uint64, // This output isn't owned by one of the given addresses. Ignore. continue } - totalAmountStaked, err = math.Add64(totalAmountStaked, stake.Out.Amount()) + + assetID := output.AssetID() + newAmount, err := math.Add64(totalAmountStaked[assetID], secpOut.Amt) if err != nil { - return 0, stakedOuts, err + newAmount = stdmath.MaxUint64 } + totalAmountStaked[assetID] = newAmount + stakedOuts = append( stakedOuts, - *stake, + *output, ) } - return totalAmountStaked, stakedOuts, nil + return stakedOuts } // GetStake returns the amount of nAVAX that [args.Addresses] have cumulatively @@ -2071,8 +2162,8 @@ func (service *Service) GetStake(_ *http.Request, args *GetStakeArgs, response * defer currentStakerIterator.Release() var ( - totalStake uint64 - stakedOuts []avax.TransferableOutput + totalAmountStaked = make(map[ids.ID]uint64) + stakedOuts []avax.TransferableOutput ) for currentStakerIterator.Next() { // Iterates over current stakers staker := currentStakerIterator.Value() @@ -2082,15 +2173,7 @@ func (service *Service) GetStake(_ *http.Request, args *GetStakeArgs, response * return err } - stakedAmt, outs, err := service.getStakeHelper(tx, addrs) - if err != nil { - return err - } - totalStake, err = math.Add64(totalStake, stakedAmt) - if err != nil { - return err - } - stakedOuts = append(stakedOuts, outs...) + stakedOuts = append(stakedOuts, service.getStakeHelper(tx, addrs, totalAmountStaked)...) } pendingStakerIterator, err := service.vm.state.GetPendingStakerIterator() @@ -2107,18 +2190,11 @@ func (service *Service) GetStake(_ *http.Request, args *GetStakeArgs, response * return err } - stakedAmt, outs, err := service.getStakeHelper(tx, addrs) - if err != nil { - return err - } - totalStake, err = math.Add64(totalStake, stakedAmt) - if err != nil { - return err - } - stakedOuts = append(stakedOuts, outs...) + stakedOuts = append(stakedOuts, service.getStakeHelper(tx, addrs, totalAmountStaked)...) } - response.Staked = json.Uint64(totalStake) + response.Stakeds = newJSONBalanceMap(totalAmountStaked) + response.Staked = response.Stakeds[service.vm.ctx.AVAXAssetID] response.Outputs = make([]string, len(stakedOuts)) for i, output := range stakedOuts { bytes, err := txs.Codec.Marshal(txs.Version, output) @@ -2135,6 +2211,11 @@ func (service *Service) GetStake(_ *http.Request, args *GetStakeArgs, response * return nil } +// GetMinStakeArgs are the arguments for calling GetMinStake. +type GetMinStakeArgs struct { + SubnetID ids.ID `json:"subnetID"` +} + // GetMinStakeReply is the response from calling GetMinStake. type GetMinStakeReply struct { // The minimum amount of tokens one must bond to be a validator @@ -2144,11 +2225,34 @@ type GetMinStakeReply struct { } // GetMinStake returns the minimum staking amount in nAVAX. -func (service *Service) GetMinStake(_ *http.Request, _ *struct{}, reply *GetMinStakeReply) error { - timestamp := service.vm.state.GetTimestamp() - minValidatorStake, _, minDelegatorStake, _, _, _, _, _, _, _ := executor.GetCurrentInflationSettings(timestamp, service.vm.ctx.NetworkID, &service.vm.Config) - reply.MinValidatorStake = json.Uint64(minValidatorStake) - reply.MinDelegatorStake = json.Uint64(minDelegatorStake) +func (service *Service) GetMinStake(_ *http.Request, args *GetMinStakeArgs, reply *GetMinStakeReply) error { + if args.SubnetID == constants.PrimaryNetworkID { + timestamp := service.vm.state.GetTimestamp() + minValidatorStake, _, minDelegatorStake, _, _, _, _, _, _, _ := executor.GetCurrentInflationSettings(timestamp, service.vm.ctx.NetworkID, &service.vm.Config) + reply.MinValidatorStake = json.Uint64(minValidatorStake) + reply.MinDelegatorStake = json.Uint64(minDelegatorStake) + return nil + } + + transformSubnetIntf, err := service.vm.state.GetSubnetTransformation(args.SubnetID) + if err != nil { + return fmt.Errorf( + "failed fetching subnet transformation for %s: %w", + args.SubnetID, + err, + ) + } + transformSubnet, ok := transformSubnetIntf.Unsigned.(*txs.TransformSubnetTx) + if !ok { + return fmt.Errorf( + "unexpected subnet transformation tx type fetched %T", + transformSubnetIntf.Unsigned, + ) + } + + reply.MinValidatorStake = json.Uint64(transformSubnet.MinValidatorStake) + reply.MinDelegatorStake = json.Uint64(transformSubnet.MinDelegatorStake) + return nil } @@ -2161,8 +2265,9 @@ type GetTotalStakeArgs struct { // GetTotalStakeReply is the response from calling GetTotalStake. type GetTotalStakeReply struct { - Stake json.Uint64 `json:"stake,omitempty"` - Weight json.Uint64 `json:"weight,omitempty"` + // TODO: deprecate one of these fields + Stake json.Uint64 `json:"stake"` + Weight json.Uint64 `json:"weight"` } // GetTotalStake returns the total amount staked on the Primary Network @@ -2172,11 +2277,8 @@ func (service *Service) GetTotalStake(_ *http.Request, args *GetTotalStakeArgs, return errNoValidators } weight := json.Uint64(vdrs.Weight()) - if args.SubnetID == constants.PrimaryNetworkID { - reply.Stake = weight - } else { - reply.Weight = weight - } + reply.Weight = weight + reply.Stake = weight return nil } @@ -2250,7 +2352,7 @@ func (service *Service) GetRewardUTXOs(_ *http.Request, args *api.GetTxArgs, rep reply.NumFetched = json.Uint64(len(utxos)) reply.UTXOs = make([]string, len(utxos)) for i, utxo := range utxos { - utxoBytes, err := blocks.GenesisCodec.Marshal(txs.Version, utxo) + utxoBytes, err := txs.GenesisCodec.Marshal(txs.Version, utxo) if err != nil { return fmt.Errorf("failed to encode UTXO to bytes: %w", err) } @@ -2313,13 +2415,14 @@ func (service *Service) GetBlock(_ *http.Request, args *api.GetBlockArgs, respon zap.Stringer("encoding", args.Encoding), ) - block, err := service.vm.GetBlock(args.BlockID) + block, err := service.vm.manager.GetStatelessBlock(args.BlockID) if err != nil { return fmt.Errorf("couldn't get block with id %s: %w", args.BlockID, err) } response.Encoding = args.Encoding if args.Encoding == formatting.JSON { + block.InitCtx(service.vm.ctx) response.Block = block return nil } diff --git a/avalanchego/vms/platformvm/service_test.go b/avalanchego/vms/platformvm/service_test.go index baf533a8..bd0f8d51 100644 --- a/avalanchego/vms/platformvm/service_test.go +++ b/avalanchego/vms/platformvm/service_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm @@ -376,7 +376,7 @@ func TestGetTx(t *testing.T) { t.Fatalf("failed test '%s - %s': %s", test.description, encoding.String(), err) } commit := options[0].(*blockexecutor.Block) - if _, ok := commit.Block.(*blocks.CommitBlock); !ok { + if _, ok := commit.Block.(*blocks.ApricotCommitBlock); !ok { t.Fatalf("failed test '%s - %s': should prefer to commit", test.description, encoding.String()) } if err := commit.Verify(); err != nil { @@ -542,10 +542,11 @@ func TestGetStake(t *testing.T) { ) require.NoError(err) - staker := state.NewPrimaryNetworkStaker(tx.ID(), &tx.Unsigned.(*txs.AddDelegatorTx).Validator) - staker.PotentialReward = 0 - staker.NextTime = staker.EndTime - staker.Priority = state.PrimaryNetworkDelegatorCurrentPriority + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddDelegatorTx), + 0, + ) service.vm.state.PutCurrentDelegator(staker) service.vm.state.AddTx(tx, status.Committed) @@ -589,9 +590,10 @@ func TestGetStake(t *testing.T) { ) require.NoError(err) - staker = state.NewPrimaryNetworkStaker(tx.ID(), &tx.Unsigned.(*txs.AddValidatorTx).Validator) - staker.NextTime = staker.StartTime - staker.Priority = state.PrimaryNetworkValidatorPendingPriority + staker = state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddValidatorTx), + ) service.vm.state.PutPendingValidator(staker) service.vm.state.AddTx(tx, status.Committed) @@ -644,10 +646,10 @@ func TestGetCurrentValidators(t *testing.T) { for _, vdr := range genesis.Validators { found := false for i := 0; i < len(response.Validators) && !found; i++ { - gotVdr, ok := response.Validators[i].(pchainapi.PrimaryValidator) + gotVdr, ok := response.Validators[i].(pchainapi.PermissionlessValidator) switch { case !ok: - t.Fatal("expected pchainapi.PrimaryValidator") + t.Fatal("expected pchainapi.PermissionlessValidator") case gotVdr.NodeID != vdr.NodeID: case gotVdr.EndTime != vdr.EndTime: t.Fatalf("expected end time of %s to be %v but got %v", @@ -695,10 +697,11 @@ func TestGetCurrentValidators(t *testing.T) { t.Fatal(err) } - staker := state.NewPrimaryNetworkStaker(tx.ID(), &tx.Unsigned.(*txs.AddDelegatorTx).Validator) - staker.PotentialReward = 0 - staker.NextTime = staker.EndTime - staker.Priority = state.PrimaryNetworkDelegatorCurrentPriority + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddDelegatorTx), + 0, + ) service.vm.state.PutCurrentDelegator(staker) service.vm.state.AddTx(tx, status.Committed) @@ -720,7 +723,7 @@ func TestGetCurrentValidators(t *testing.T) { // Make sure the delegator is there found := false for i := 0; i < len(response.Validators) && !found; i++ { - vdr := response.Validators[i].(pchainapi.PrimaryValidator) + vdr := response.Validators[i].(pchainapi.PermissionlessValidator) if vdr.NodeID != validatorNodeID { continue } @@ -786,6 +789,8 @@ func TestGetBlock(t *testing.T) { service.vm.ctx.Lock.Lock() defer service.vm.ctx.Lock.Unlock() + service.vm.Config.CreateAssetTxFee = 100 * defaultTxFee + // Make a block an accept it, then check we can get it. tx, err := service.vm.txBuilder.NewCreateChainTx( // Test GetTx works for standard blocks testSubnet1.ID(), @@ -803,7 +808,7 @@ func TestGetBlock(t *testing.T) { if err != nil { t.Fatal(err) } - statelessBlock, err := blocks.NewStandardBlock( + statelessBlock, err := blocks.NewApricotStandardBlock( preferred.ID(), preferred.Height()+1, []*txs.Tx{tx}, @@ -830,7 +835,10 @@ func TestGetBlock(t *testing.T) { switch { case test.encoding == formatting.JSON: - require.Equal(t, block, response.Block) + require.Equal(t, statelessBlock, response.Block) + + _, err = stdjson.Marshal(response) + require.NoError(t, err) default: decoded, _ := formatting.Decode(response.Encoding, response.Block.(string)) require.Equal(t, block.Bytes(), decoded) diff --git a/avalanchego/vms/platformvm/signer/empty.go b/avalanchego/vms/platformvm/signer/empty.go new file mode 100644 index 00000000..4e2eba8a --- /dev/null +++ b/avalanchego/vms/platformvm/signer/empty.go @@ -0,0 +1,15 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package signer + +import ( + "github.com/ava-labs/avalanchego/utils/crypto/bls" +) + +var _ Signer = &Empty{} + +type Empty struct{} + +func (*Empty) Verify() error { return nil } +func (*Empty) Key() *bls.PublicKey { return nil } diff --git a/avalanchego/vms/platformvm/signer/empty_test.go b/avalanchego/vms/platformvm/signer/empty_test.go new file mode 100644 index 00000000..3a014a00 --- /dev/null +++ b/avalanchego/vms/platformvm/signer/empty_test.go @@ -0,0 +1,18 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package signer + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEmpty(t *testing.T) { + require := require.New(t) + + noSigner := &Empty{} + require.NoError(noSigner.Verify()) + require.Nil(noSigner.Key()) +} diff --git a/avalanchego/vms/platformvm/signer/proof_of_possession.go b/avalanchego/vms/platformvm/signer/proof_of_possession.go new file mode 100644 index 00000000..0297c753 --- /dev/null +++ b/avalanchego/vms/platformvm/signer/proof_of_possession.go @@ -0,0 +1,109 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package signer + +import ( + "encoding/json" + "errors" + + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/formatting" +) + +var ( + _ Signer = &ProofOfPossession{} + + errInvalidProofOfPossession = errors.New("invalid proof of possession") +) + +type ProofOfPossession struct { + PublicKey [bls.PublicKeyLen]byte `serialize:"true" json:"publicKey"` + // BLS signature proving ownership of [PublicKey]. The signed message is the + // [PublicKey]. + ProofOfPossession [bls.SignatureLen]byte `serialize:"true" json:"proofOfPossession"` + + // publicKey is the parsed version of [PublicKey]. It is populated in + // [Verify]. + publicKey *bls.PublicKey +} + +func NewProofOfPossession(sk *bls.SecretKey) *ProofOfPossession { + pk := bls.PublicFromSecretKey(sk) + pkBytes := bls.PublicKeyToBytes(pk) + sig := bls.SignProofOfPossession(sk, pkBytes) + sigBytes := bls.SignatureToBytes(sig) + + pop := &ProofOfPossession{ + publicKey: pk, + } + copy(pop.PublicKey[:], pkBytes) + copy(pop.ProofOfPossession[:], sigBytes) + return pop +} + +func (p *ProofOfPossession) Verify() error { + publicKey, err := bls.PublicKeyFromBytes(p.PublicKey[:]) + if err != nil { + return err + } + signature, err := bls.SignatureFromBytes(p.ProofOfPossession[:]) + if err != nil { + return err + } + if !bls.VerifyProofOfPossession(publicKey, signature, p.PublicKey[:]) { + return errInvalidProofOfPossession + } + + p.publicKey = publicKey + return nil +} + +func (p *ProofOfPossession) Key() *bls.PublicKey { return p.publicKey } + +type jsonProofOfPossession struct { + PublicKey string `json:"publicKey"` + ProofOfPossession string `json:"proofOfPossession"` +} + +func (p *ProofOfPossession) MarshalJSON() ([]byte, error) { + pk, err := formatting.Encode(formatting.HexNC, p.PublicKey[:]) + if err != nil { + return nil, err + } + pop, err := formatting.Encode(formatting.HexNC, p.ProofOfPossession[:]) + if err != nil { + return nil, err + } + return json.Marshal(jsonProofOfPossession{ + PublicKey: pk, + ProofOfPossession: pop, + }) +} + +func (p *ProofOfPossession) UnmarshalJSON(b []byte) error { + jsonBLS := jsonProofOfPossession{} + err := json.Unmarshal(b, &jsonBLS) + if err != nil { + return err + } + + pkBytes, err := formatting.Decode(formatting.HexNC, jsonBLS.PublicKey) + if err != nil { + return err + } + pk, err := bls.PublicKeyFromBytes(pkBytes) + if err != nil { + return err + } + + popBytes, err := formatting.Decode(formatting.HexNC, jsonBLS.ProofOfPossession) + if err != nil { + return err + } + + copy(p.PublicKey[:], pkBytes) + copy(p.ProofOfPossession[:], popBytes) + p.publicKey = pk + return nil +} diff --git a/avalanchego/vms/platformvm/signer/proof_of_possession_test.go b/avalanchego/vms/platformvm/signer/proof_of_possession_test.go new file mode 100644 index 00000000..2c9ea6bc --- /dev/null +++ b/avalanchego/vms/platformvm/signer/proof_of_possession_test.go @@ -0,0 +1,55 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package signer + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/utils/crypto/bls" +) + +func TestProofOfPossession(t *testing.T) { + require := require.New(t) + + blsPOP, err := newProofOfPossession() + require.NoError(err) + require.NoError(blsPOP.Verify()) + require.NotNil(blsPOP.Key()) + + blsPOP, err = newProofOfPossession() + require.NoError(err) + blsPOP.ProofOfPossession = [bls.SignatureLen]byte{} + require.Error(blsPOP.Verify()) + + blsPOP, err = newProofOfPossession() + require.NoError(err) + blsPOP.PublicKey = [bls.PublicKeyLen]byte{} + require.Error(blsPOP.Verify()) + + newBLSPOP, err := newProofOfPossession() + require.NoError(err) + newBLSPOP.ProofOfPossession = blsPOP.ProofOfPossession + require.ErrorIs(newBLSPOP.Verify(), errInvalidProofOfPossession) +} + +func TestNewProofOfPossessionDeterministic(t *testing.T) { + require := require.New(t) + + sk, err := bls.NewSecretKey() + require.NoError(err) + + blsPOP0 := NewProofOfPossession(sk) + blsPOP1 := NewProofOfPossession(sk) + require.Equal(blsPOP0, blsPOP1) +} + +func newProofOfPossession() (*ProofOfPossession, error) { + sk, err := bls.NewSecretKey() + if err != nil { + return nil, err + } + return NewProofOfPossession(sk), nil +} diff --git a/avalanchego/vms/platformvm/signer/signer.go b/avalanchego/vms/platformvm/signer/signer.go new file mode 100644 index 00000000..71259587 --- /dev/null +++ b/avalanchego/vms/platformvm/signer/signer.go @@ -0,0 +1,18 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package signer + +import ( + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/vms/components/verify" +) + +type Signer interface { + verify.Verifiable + + // Key returns the public BLS key if it exists. + // Note: [nil] will be returned if the key does not exist. + // Invariant: Only called after [Verify] returns [nil]. + Key() *bls.PublicKey +} diff --git a/avalanchego/vms/platformvm/stakeable/stakeable_lock.go b/avalanchego/vms/platformvm/stakeable/stakeable_lock.go index f66cb1dc..b3d6c72c 100644 --- a/avalanchego/vms/platformvm/stakeable/stakeable_lock.go +++ b/avalanchego/vms/platformvm/stakeable/stakeable_lock.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package stakeable diff --git a/avalanchego/vms/platformvm/state/diff.go b/avalanchego/vms/platformvm/state/diff.go index 9843e23a..e557f25c 100644 --- a/avalanchego/vms/platformvm/state/diff.go +++ b/avalanchego/vms/platformvm/state/diff.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -36,13 +36,16 @@ type diff struct { timestamp time.Time - currentSupply uint64 + // Subnet ID --> supply of native asset of the subnet + currentSupply map[ids.ID]uint64 currentStakerDiffs diffStakers pendingStakerDiffs diffStakers - addedSubnets []*txs.Tx - cachedSubnets []*txs.Tx + addedSubnets []*txs.Tx + // Subnet ID --> Tx that transforms the subnet + transformedSubnets map[ids.ID]*txs.Tx + cachedSubnets []*txs.Tx addedChains map[ids.ID][]*txs.Tx cachedChains map[ids.ID][]*txs.Tx @@ -75,7 +78,6 @@ func NewDiff( parentID: parentID, stateVersions: stateVersions, timestamp: parentState.GetTimestamp(), - currentSupply: parentState.GetCurrentSupply(), }, nil } @@ -91,12 +93,28 @@ func (d *diff) SetTimestamp(timestamp time.Time) { d.timestamp = timestamp } -func (d *diff) GetCurrentSupply() uint64 { - return d.currentSupply +func (d *diff) GetCurrentSupply(subnetID ids.ID) (uint64, error) { + supply, ok := d.currentSupply[subnetID] + if ok { + return supply, nil + } + + // If the subnet supply wasn't modified in this diff, ask the parent state. + parentState, ok := d.stateVersions.GetState(d.parentID) + if !ok { + return 0, fmt.Errorf("%w: %s", ErrMissingParentState, d.parentID) + } + return parentState.GetCurrentSupply(subnetID) } -func (d *diff) SetCurrentSupply(currentSupply uint64) { - d.currentSupply = currentSupply +func (d *diff) SetCurrentSupply(subnetID ids.ID, currentSupply uint64) { + if d.currentSupply == nil { + d.currentSupply = map[ids.ID]uint64{ + subnetID: currentSupply, + } + } else { + d.currentSupply[subnetID] = currentSupply + } } func (d *diff) GetCurrentValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { @@ -262,6 +280,31 @@ func (d *diff) AddSubnet(createSubnetTx *txs.Tx) { } } +func (d *diff) GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) { + tx, exists := d.transformedSubnets[subnetID] + if exists { + return tx, nil + } + + // If the subnet wasn't transformed in this diff, ask the parent state. + parentState, ok := d.stateVersions.GetState(d.parentID) + if !ok { + return nil, ErrMissingParentState + } + return parentState.GetSubnetTransformation(subnetID) +} + +func (d *diff) AddSubnetTransformation(transformSubnetTxIntf *txs.Tx) { + transformSubnetTx := transformSubnetTxIntf.Unsigned.(*txs.TransformSubnetTx) + if d.transformedSubnets == nil { + d.transformedSubnets = map[ids.ID]*txs.Tx{ + transformSubnetTx.Subnet: transformSubnetTxIntf, + } + } else { + d.transformedSubnets[transformSubnetTx.Subnet] = transformSubnetTxIntf + } +} + func (d *diff) GetChains(subnetID ids.ID) ([]*txs.Tx, error) { addedChains := d.addedChains[subnetID] if len(addedChains) == 0 { @@ -411,7 +454,9 @@ func (d *diff) DeleteUTXO(utxoID ids.ID) { func (d *diff) Apply(baseState State) { baseState.SetTimestamp(d.timestamp) - baseState.SetCurrentSupply(d.currentSupply) + for subnetID, supply := range d.currentSupply { + baseState.SetCurrentSupply(subnetID, supply) + } for _, subnetValidatorDiffs := range d.currentStakerDiffs.validatorDiffs { for _, validatorDiff := range subnetValidatorDiffs { if validatorDiff.validatorModified { @@ -457,6 +502,9 @@ func (d *diff) Apply(baseState State) { for _, subnet := range d.addedSubnets { baseState.AddSubnet(subnet) } + for _, tx := range d.transformedSubnets { + baseState.AddSubnetTransformation(tx) + } for _, chains := range d.addedChains { for _, chain := range chains { baseState.AddChain(chain) diff --git a/avalanchego/vms/platformvm/state/diff_test.go b/avalanchego/vms/platformvm/state/diff_test.go index f0407d7a..9d0aa0d8 100644 --- a/avalanchego/vms/platformvm/state/diff_test.go +++ b/avalanchego/vms/platformvm/state/diff_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -14,6 +14,7 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" @@ -61,11 +62,19 @@ func TestDiffCurrentSupply(t *testing.T) { d, err := NewDiff(lastAcceptedID, versions) require.NoError(err) - initialCurrentSupply := d.GetCurrentSupply() + initialCurrentSupply, err := d.GetCurrentSupply(constants.PrimaryNetworkID) + require.NoError(err) + newCurrentSupply := initialCurrentSupply + 1 - d.SetCurrentSupply(newCurrentSupply) - require.Equal(newCurrentSupply, d.GetCurrentSupply()) - require.Equal(initialCurrentSupply, state.GetCurrentSupply()) + d.SetCurrentSupply(constants.PrimaryNetworkID, newCurrentSupply) + + returnedNewCurrentSupply, err := d.GetCurrentSupply(constants.PrimaryNetworkID) + require.NoError(err) + require.Equal(newCurrentSupply, returnedNewCurrentSupply) + + returnedBaseCurrentSupply, err := state.GetCurrentSupply(constants.PrimaryNetworkID) + require.NoError(err) + require.Equal(initialCurrentSupply, returnedBaseCurrentSupply) } func TestDiffCurrentValidator(t *testing.T) { @@ -77,7 +86,6 @@ func TestDiffCurrentValidator(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) states.EXPECT().GetState(lastAcceptedID).Return(state, true).AnyTimes() @@ -115,7 +123,6 @@ func TestDiffPendingValidator(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) states.EXPECT().GetState(lastAcceptedID).Return(state, true).AnyTimes() @@ -158,7 +165,6 @@ func TestDiffCurrentDelegator(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) lastAcceptedID := ids.GenerateTestID() @@ -209,7 +215,6 @@ func TestDiffPendingDelegator(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) lastAcceptedID := ids.GenerateTestID() @@ -254,7 +259,6 @@ func TestDiffSubnet(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) lastAcceptedID := ids.GenerateTestID() @@ -286,7 +290,6 @@ func TestDiffChain(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) lastAcceptedID := ids.GenerateTestID() @@ -327,7 +330,6 @@ func TestDiffTx(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) lastAcceptedID := ids.GenerateTestID() @@ -379,7 +381,6 @@ func TestDiffRewardUTXO(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) lastAcceptedID := ids.GenerateTestID() @@ -426,7 +427,6 @@ func TestDiffUTXO(t *testing.T) { state := NewMockState(ctrl) // Called in NewDiff state.EXPECT().GetTimestamp().Return(time.Now()).Times(1) - state.EXPECT().GetCurrentSupply().Return(uint64(1337)).Times(1) states := NewMockVersions(ctrl) lastAcceptedID := ids.GenerateTestID() @@ -488,7 +488,14 @@ func assertChainsEqual(t *testing.T, expected, actual Chain) { } require.Equal(t, expected.GetTimestamp(), actual.GetTimestamp()) - require.Equal(t, expected.GetCurrentSupply(), actual.GetCurrentSupply()) + + expectedCurrentSupply, err := expected.GetCurrentSupply(constants.PrimaryNetworkID) + require.NoError(t, err) + + actualCurrentSupply, err := actual.GetCurrentSupply(constants.PrimaryNetworkID) + require.NoError(t, err) + + require.Equal(t, expectedCurrentSupply, actualCurrentSupply) expectedSubnets, expectedErr := expected.GetSubnets() actualSubnets, actualErr := actual.GetSubnets() diff --git a/avalanchego/vms/platformvm/state/empty_iterator.go b/avalanchego/vms/platformvm/state/empty_iterator.go index 241e23d1..6998f29c 100644 --- a/avalanchego/vms/platformvm/state/empty_iterator.go +++ b/avalanchego/vms/platformvm/state/empty_iterator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/empty_iterator_test.go b/avalanchego/vms/platformvm/state/empty_iterator_test.go index 69626943..1e43252e 100644 --- a/avalanchego/vms/platformvm/state/empty_iterator_test.go +++ b/avalanchego/vms/platformvm/state/empty_iterator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/masked_iterator.go b/avalanchego/vms/platformvm/state/masked_iterator.go index d84b98f9..c8bd4c19 100644 --- a/avalanchego/vms/platformvm/state/masked_iterator.go +++ b/avalanchego/vms/platformvm/state/masked_iterator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/masked_iterator_test.go b/avalanchego/vms/platformvm/state/masked_iterator_test.go index 74e1ae2d..d986e036 100644 --- a/avalanchego/vms/platformvm/state/masked_iterator_test.go +++ b/avalanchego/vms/platformvm/state/masked_iterator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/merged_iterator.go b/avalanchego/vms/platformvm/state/merged_iterator.go index 80d6834d..4f128fa7 100644 --- a/avalanchego/vms/platformvm/state/merged_iterator.go +++ b/avalanchego/vms/platformvm/state/merged_iterator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/merged_iterator_test.go b/avalanchego/vms/platformvm/state/merged_iterator_test.go index aa82feca..7adaa7bd 100644 --- a/avalanchego/vms/platformvm/state/merged_iterator_test.go +++ b/avalanchego/vms/platformvm/state/merged_iterator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/mock_chain.go b/avalanchego/vms/platformvm/state/mock_chain.go new file mode 100644 index 00000000..80fd409c --- /dev/null +++ b/avalanchego/vms/platformvm/state/mock_chain.go @@ -0,0 +1,453 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/vms/platformvm/state (interfaces: Chain) + +// Package state is a generated GoMock package. +package state + +import ( + reflect "reflect" + time "time" + + ids "github.com/ava-labs/avalanchego/ids" + avax "github.com/ava-labs/avalanchego/vms/components/avax" + status "github.com/ava-labs/avalanchego/vms/platformvm/status" + txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" + gomock "github.com/golang/mock/gomock" +) + +// MockChain is a mock of Chain interface. +type MockChain struct { + ctrl *gomock.Controller + recorder *MockChainMockRecorder +} + +// MockChainMockRecorder is the mock recorder for MockChain. +type MockChainMockRecorder struct { + mock *MockChain +} + +// NewMockChain creates a new mock instance. +func NewMockChain(ctrl *gomock.Controller) *MockChain { + mock := &MockChain{ctrl: ctrl} + mock.recorder = &MockChainMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockChain) EXPECT() *MockChainMockRecorder { + return m.recorder +} + +// AddChain mocks base method. +func (m *MockChain) AddChain(arg0 *txs.Tx) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddChain", arg0) +} + +// AddChain indicates an expected call of AddChain. +func (mr *MockChainMockRecorder) AddChain(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChain", reflect.TypeOf((*MockChain)(nil).AddChain), arg0) +} + +// AddRewardUTXO mocks base method. +func (m *MockChain) AddRewardUTXO(arg0 ids.ID, arg1 *avax.UTXO) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddRewardUTXO", arg0, arg1) +} + +// AddRewardUTXO indicates an expected call of AddRewardUTXO. +func (mr *MockChainMockRecorder) AddRewardUTXO(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewardUTXO", reflect.TypeOf((*MockChain)(nil).AddRewardUTXO), arg0, arg1) +} + +// AddSubnet mocks base method. +func (m *MockChain) AddSubnet(arg0 *txs.Tx) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddSubnet", arg0) +} + +// AddSubnet indicates an expected call of AddSubnet. +func (mr *MockChainMockRecorder) AddSubnet(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnet", reflect.TypeOf((*MockChain)(nil).AddSubnet), arg0) +} + +// AddSubnetTransformation mocks base method. +func (m *MockChain) AddSubnetTransformation(arg0 *txs.Tx) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddSubnetTransformation", arg0) +} + +// AddSubnetTransformation indicates an expected call of AddSubnetTransformation. +func (mr *MockChainMockRecorder) AddSubnetTransformation(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnetTransformation", reflect.TypeOf((*MockChain)(nil).AddSubnetTransformation), arg0) +} + +// AddTx mocks base method. +func (m *MockChain) AddTx(arg0 *txs.Tx, arg1 status.Status) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddTx", arg0, arg1) +} + +// AddTx indicates an expected call of AddTx. +func (mr *MockChainMockRecorder) AddTx(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTx", reflect.TypeOf((*MockChain)(nil).AddTx), arg0, arg1) +} + +// AddUTXO mocks base method. +func (m *MockChain) AddUTXO(arg0 *avax.UTXO) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddUTXO", arg0) +} + +// AddUTXO indicates an expected call of AddUTXO. +func (mr *MockChainMockRecorder) AddUTXO(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUTXO", reflect.TypeOf((*MockChain)(nil).AddUTXO), arg0) +} + +// DeleteCurrentDelegator mocks base method. +func (m *MockChain) DeleteCurrentDelegator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteCurrentDelegator", arg0) +} + +// DeleteCurrentDelegator indicates an expected call of DeleteCurrentDelegator. +func (mr *MockChainMockRecorder) DeleteCurrentDelegator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentDelegator", reflect.TypeOf((*MockChain)(nil).DeleteCurrentDelegator), arg0) +} + +// DeleteCurrentValidator mocks base method. +func (m *MockChain) DeleteCurrentValidator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteCurrentValidator", arg0) +} + +// DeleteCurrentValidator indicates an expected call of DeleteCurrentValidator. +func (mr *MockChainMockRecorder) DeleteCurrentValidator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentValidator", reflect.TypeOf((*MockChain)(nil).DeleteCurrentValidator), arg0) +} + +// DeletePendingDelegator mocks base method. +func (m *MockChain) DeletePendingDelegator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeletePendingDelegator", arg0) +} + +// DeletePendingDelegator indicates an expected call of DeletePendingDelegator. +func (mr *MockChainMockRecorder) DeletePendingDelegator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingDelegator", reflect.TypeOf((*MockChain)(nil).DeletePendingDelegator), arg0) +} + +// DeletePendingValidator mocks base method. +func (m *MockChain) DeletePendingValidator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeletePendingValidator", arg0) +} + +// DeletePendingValidator indicates an expected call of DeletePendingValidator. +func (mr *MockChainMockRecorder) DeletePendingValidator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingValidator", reflect.TypeOf((*MockChain)(nil).DeletePendingValidator), arg0) +} + +// DeleteUTXO mocks base method. +func (m *MockChain) DeleteUTXO(arg0 ids.ID) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteUTXO", arg0) +} + +// DeleteUTXO indicates an expected call of DeleteUTXO. +func (mr *MockChainMockRecorder) DeleteUTXO(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUTXO", reflect.TypeOf((*MockChain)(nil).DeleteUTXO), arg0) +} + +// GetChains mocks base method. +func (m *MockChain) GetChains(arg0 ids.ID) ([]*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetChains", arg0) + ret0, _ := ret[0].([]*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetChains indicates an expected call of GetChains. +func (mr *MockChainMockRecorder) GetChains(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChains", reflect.TypeOf((*MockChain)(nil).GetChains), arg0) +} + +// GetCurrentDelegatorIterator mocks base method. +func (m *MockChain) GetCurrentDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCurrentDelegatorIterator", arg0, arg1) + ret0, _ := ret[0].(StakerIterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCurrentDelegatorIterator indicates an expected call of GetCurrentDelegatorIterator. +func (mr *MockChainMockRecorder) GetCurrentDelegatorIterator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentDelegatorIterator", reflect.TypeOf((*MockChain)(nil).GetCurrentDelegatorIterator), arg0, arg1) +} + +// GetCurrentStakerIterator mocks base method. +func (m *MockChain) GetCurrentStakerIterator() (StakerIterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCurrentStakerIterator") + ret0, _ := ret[0].(StakerIterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCurrentStakerIterator indicates an expected call of GetCurrentStakerIterator. +func (mr *MockChainMockRecorder) GetCurrentStakerIterator() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentStakerIterator", reflect.TypeOf((*MockChain)(nil).GetCurrentStakerIterator)) +} + +// GetCurrentSupply mocks base method. +func (m *MockChain) GetCurrentSupply(arg0 ids.ID) (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCurrentSupply", arg0) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCurrentSupply indicates an expected call of GetCurrentSupply. +func (mr *MockChainMockRecorder) GetCurrentSupply(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSupply", reflect.TypeOf((*MockChain)(nil).GetCurrentSupply), arg0) +} + +// GetCurrentValidator mocks base method. +func (m *MockChain) GetCurrentValidator(arg0 ids.ID, arg1 ids.NodeID) (*Staker, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCurrentValidator", arg0, arg1) + ret0, _ := ret[0].(*Staker) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCurrentValidator indicates an expected call of GetCurrentValidator. +func (mr *MockChainMockRecorder) GetCurrentValidator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidator", reflect.TypeOf((*MockChain)(nil).GetCurrentValidator), arg0, arg1) +} + +// GetPendingDelegatorIterator mocks base method. +func (m *MockChain) GetPendingDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPendingDelegatorIterator", arg0, arg1) + ret0, _ := ret[0].(StakerIterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPendingDelegatorIterator indicates an expected call of GetPendingDelegatorIterator. +func (mr *MockChainMockRecorder) GetPendingDelegatorIterator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDelegatorIterator", reflect.TypeOf((*MockChain)(nil).GetPendingDelegatorIterator), arg0, arg1) +} + +// GetPendingStakerIterator mocks base method. +func (m *MockChain) GetPendingStakerIterator() (StakerIterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPendingStakerIterator") + ret0, _ := ret[0].(StakerIterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPendingStakerIterator indicates an expected call of GetPendingStakerIterator. +func (mr *MockChainMockRecorder) GetPendingStakerIterator() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingStakerIterator", reflect.TypeOf((*MockChain)(nil).GetPendingStakerIterator)) +} + +// GetPendingValidator mocks base method. +func (m *MockChain) GetPendingValidator(arg0 ids.ID, arg1 ids.NodeID) (*Staker, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPendingValidator", arg0, arg1) + ret0, _ := ret[0].(*Staker) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPendingValidator indicates an expected call of GetPendingValidator. +func (mr *MockChainMockRecorder) GetPendingValidator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockChain)(nil).GetPendingValidator), arg0, arg1) +} + +// GetRewardUTXOs mocks base method. +func (m *MockChain) GetRewardUTXOs(arg0 ids.ID) ([]*avax.UTXO, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRewardUTXOs", arg0) + ret0, _ := ret[0].([]*avax.UTXO) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRewardUTXOs indicates an expected call of GetRewardUTXOs. +func (mr *MockChainMockRecorder) GetRewardUTXOs(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRewardUTXOs", reflect.TypeOf((*MockChain)(nil).GetRewardUTXOs), arg0) +} + +// GetSubnetTransformation mocks base method. +func (m *MockChain) GetSubnetTransformation(arg0 ids.ID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSubnetTransformation", arg0) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSubnetTransformation indicates an expected call of GetSubnetTransformation. +func (mr *MockChainMockRecorder) GetSubnetTransformation(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetTransformation", reflect.TypeOf((*MockChain)(nil).GetSubnetTransformation), arg0) +} + +// GetSubnets mocks base method. +func (m *MockChain) GetSubnets() ([]*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSubnets") + ret0, _ := ret[0].([]*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSubnets indicates an expected call of GetSubnets. +func (mr *MockChainMockRecorder) GetSubnets() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnets", reflect.TypeOf((*MockChain)(nil).GetSubnets)) +} + +// GetTimestamp mocks base method. +func (m *MockChain) GetTimestamp() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTimestamp") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetTimestamp indicates an expected call of GetTimestamp. +func (mr *MockChainMockRecorder) GetTimestamp() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimestamp", reflect.TypeOf((*MockChain)(nil).GetTimestamp)) +} + +// GetTx mocks base method. +func (m *MockChain) GetTx(arg0 ids.ID) (*txs.Tx, status.Status, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTx", arg0) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(status.Status) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetTx indicates an expected call of GetTx. +func (mr *MockChainMockRecorder) GetTx(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTx", reflect.TypeOf((*MockChain)(nil).GetTx), arg0) +} + +// GetUTXO mocks base method. +func (m *MockChain) GetUTXO(arg0 ids.ID) (*avax.UTXO, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUTXO", arg0) + ret0, _ := ret[0].(*avax.UTXO) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUTXO indicates an expected call of GetUTXO. +func (mr *MockChainMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockChain)(nil).GetUTXO), arg0) +} + +// PutCurrentDelegator mocks base method. +func (m *MockChain) PutCurrentDelegator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PutCurrentDelegator", arg0) +} + +// PutCurrentDelegator indicates an expected call of PutCurrentDelegator. +func (mr *MockChainMockRecorder) PutCurrentDelegator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentDelegator", reflect.TypeOf((*MockChain)(nil).PutCurrentDelegator), arg0) +} + +// PutCurrentValidator mocks base method. +func (m *MockChain) PutCurrentValidator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PutCurrentValidator", arg0) +} + +// PutCurrentValidator indicates an expected call of PutCurrentValidator. +func (mr *MockChainMockRecorder) PutCurrentValidator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentValidator", reflect.TypeOf((*MockChain)(nil).PutCurrentValidator), arg0) +} + +// PutPendingDelegator mocks base method. +func (m *MockChain) PutPendingDelegator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PutPendingDelegator", arg0) +} + +// PutPendingDelegator indicates an expected call of PutPendingDelegator. +func (mr *MockChainMockRecorder) PutPendingDelegator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingDelegator", reflect.TypeOf((*MockChain)(nil).PutPendingDelegator), arg0) +} + +// PutPendingValidator mocks base method. +func (m *MockChain) PutPendingValidator(arg0 *Staker) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PutPendingValidator", arg0) +} + +// PutPendingValidator indicates an expected call of PutPendingValidator. +func (mr *MockChainMockRecorder) PutPendingValidator(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingValidator", reflect.TypeOf((*MockChain)(nil).PutPendingValidator), arg0) +} + +// SetCurrentSupply mocks base method. +func (m *MockChain) SetCurrentSupply(arg0 ids.ID, arg1 uint64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetCurrentSupply", arg0, arg1) +} + +// SetCurrentSupply indicates an expected call of SetCurrentSupply. +func (mr *MockChainMockRecorder) SetCurrentSupply(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentSupply", reflect.TypeOf((*MockChain)(nil).SetCurrentSupply), arg0, arg1) +} + +// SetTimestamp mocks base method. +func (m *MockChain) SetTimestamp(arg0 time.Time) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetTimestamp", arg0) +} + +// SetTimestamp indicates an expected call of SetTimestamp. +func (mr *MockChainMockRecorder) SetTimestamp(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimestamp", reflect.TypeOf((*MockChain)(nil).SetTimestamp), arg0) +} diff --git a/avalanchego/vms/platformvm/state/mock_diff.go b/avalanchego/vms/platformvm/state/mock_diff.go index 3356a797..9e822441 100644 --- a/avalanchego/vms/platformvm/state/mock_diff.go +++ b/avalanchego/vms/platformvm/state/mock_diff.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: vms/platformvm/state/diff.go +// Source: github.com/ava-labs/avalanchego/vms/platformvm/state (interfaces: Diff) // Package state is a generated GoMock package. package state @@ -39,9 +39,9 @@ func (m *MockDiff) EXPECT() *MockDiffMockRecorder { } // AddChain mocks base method. -func (m *MockDiff) AddChain(createChainTx *txs.Tx) { +func (m *MockDiff) AddChain(arg0 *txs.Tx) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddChain", createChainTx) + m.ctrl.Call(m, "AddChain", arg0) } func (m *MockDiff) GetNetworkID() uint32 { @@ -49,57 +49,69 @@ func (m *MockDiff) GetNetworkID() uint32 { } // AddChain indicates an expected call of AddChain. -func (mr *MockDiffMockRecorder) AddChain(createChainTx interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) AddChain(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChain", reflect.TypeOf((*MockDiff)(nil).AddChain), createChainTx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChain", reflect.TypeOf((*MockDiff)(nil).AddChain), arg0) } // AddRewardUTXO mocks base method. -func (m *MockDiff) AddRewardUTXO(txID ids.ID, utxo *avax.UTXO) { +func (m *MockDiff) AddRewardUTXO(arg0 ids.ID, arg1 *avax.UTXO) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddRewardUTXO", txID, utxo) + m.ctrl.Call(m, "AddRewardUTXO", arg0, arg1) } // AddRewardUTXO indicates an expected call of AddRewardUTXO. -func (mr *MockDiffMockRecorder) AddRewardUTXO(txID, utxo interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) AddRewardUTXO(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewardUTXO", reflect.TypeOf((*MockDiff)(nil).AddRewardUTXO), txID, utxo) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewardUTXO", reflect.TypeOf((*MockDiff)(nil).AddRewardUTXO), arg0, arg1) } // AddSubnet mocks base method. -func (m *MockDiff) AddSubnet(createSubnetTx *txs.Tx) { +func (m *MockDiff) AddSubnet(arg0 *txs.Tx) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddSubnet", createSubnetTx) + m.ctrl.Call(m, "AddSubnet", arg0) } // AddSubnet indicates an expected call of AddSubnet. -func (mr *MockDiffMockRecorder) AddSubnet(createSubnetTx interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) AddSubnet(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnet", reflect.TypeOf((*MockDiff)(nil).AddSubnet), createSubnetTx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnet", reflect.TypeOf((*MockDiff)(nil).AddSubnet), arg0) +} + +// AddSubnetTransformation mocks base method. +func (m *MockDiff) AddSubnetTransformation(arg0 *txs.Tx) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddSubnetTransformation", arg0) +} + +// AddSubnetTransformation indicates an expected call of AddSubnetTransformation. +func (mr *MockDiffMockRecorder) AddSubnetTransformation(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnetTransformation", reflect.TypeOf((*MockDiff)(nil).AddSubnetTransformation), arg0) } // AddTx mocks base method. -func (m *MockDiff) AddTx(tx *txs.Tx, status status.Status) { +func (m *MockDiff) AddTx(arg0 *txs.Tx, arg1 status.Status) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddTx", tx, status) + m.ctrl.Call(m, "AddTx", arg0, arg1) } // AddTx indicates an expected call of AddTx. -func (mr *MockDiffMockRecorder) AddTx(tx, status interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) AddTx(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTx", reflect.TypeOf((*MockDiff)(nil).AddTx), tx, status) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTx", reflect.TypeOf((*MockDiff)(nil).AddTx), arg0, arg1) } // AddUTXO mocks base method. -func (m *MockDiff) AddUTXO(utxo *avax.UTXO) { +func (m *MockDiff) AddUTXO(arg0 *avax.UTXO) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddUTXO", utxo) + m.ctrl.Call(m, "AddUTXO", arg0) } // AddUTXO indicates an expected call of AddUTXO. -func (mr *MockDiffMockRecorder) AddUTXO(utxo interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) AddUTXO(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUTXO", reflect.TypeOf((*MockDiff)(nil).AddUTXO), utxo) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUTXO", reflect.TypeOf((*MockDiff)(nil).AddUTXO), arg0) } // Apply mocks base method. @@ -115,93 +127,93 @@ func (mr *MockDiffMockRecorder) Apply(arg0 interface{}) *gomock.Call { } // DeleteCurrentDelegator mocks base method. -func (m *MockDiff) DeleteCurrentDelegator(staker *Staker) { +func (m *MockDiff) DeleteCurrentDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteCurrentDelegator", staker) + m.ctrl.Call(m, "DeleteCurrentDelegator", arg0) } // DeleteCurrentDelegator indicates an expected call of DeleteCurrentDelegator. -func (mr *MockDiffMockRecorder) DeleteCurrentDelegator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) DeleteCurrentDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentDelegator", reflect.TypeOf((*MockDiff)(nil).DeleteCurrentDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentDelegator", reflect.TypeOf((*MockDiff)(nil).DeleteCurrentDelegator), arg0) } // DeleteCurrentValidator mocks base method. -func (m *MockDiff) DeleteCurrentValidator(staker *Staker) { +func (m *MockDiff) DeleteCurrentValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteCurrentValidator", staker) + m.ctrl.Call(m, "DeleteCurrentValidator", arg0) } // DeleteCurrentValidator indicates an expected call of DeleteCurrentValidator. -func (mr *MockDiffMockRecorder) DeleteCurrentValidator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) DeleteCurrentValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentValidator", reflect.TypeOf((*MockDiff)(nil).DeleteCurrentValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentValidator", reflect.TypeOf((*MockDiff)(nil).DeleteCurrentValidator), arg0) } // DeletePendingDelegator mocks base method. -func (m *MockDiff) DeletePendingDelegator(staker *Staker) { +func (m *MockDiff) DeletePendingDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeletePendingDelegator", staker) + m.ctrl.Call(m, "DeletePendingDelegator", arg0) } // DeletePendingDelegator indicates an expected call of DeletePendingDelegator. -func (mr *MockDiffMockRecorder) DeletePendingDelegator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) DeletePendingDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingDelegator", reflect.TypeOf((*MockDiff)(nil).DeletePendingDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingDelegator", reflect.TypeOf((*MockDiff)(nil).DeletePendingDelegator), arg0) } // DeletePendingValidator mocks base method. -func (m *MockDiff) DeletePendingValidator(staker *Staker) { +func (m *MockDiff) DeletePendingValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeletePendingValidator", staker) + m.ctrl.Call(m, "DeletePendingValidator", arg0) } // DeletePendingValidator indicates an expected call of DeletePendingValidator. -func (mr *MockDiffMockRecorder) DeletePendingValidator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) DeletePendingValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingValidator", reflect.TypeOf((*MockDiff)(nil).DeletePendingValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingValidator", reflect.TypeOf((*MockDiff)(nil).DeletePendingValidator), arg0) } // DeleteUTXO mocks base method. -func (m *MockDiff) DeleteUTXO(utxoID ids.ID) { +func (m *MockDiff) DeleteUTXO(arg0 ids.ID) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteUTXO", utxoID) + m.ctrl.Call(m, "DeleteUTXO", arg0) } // DeleteUTXO indicates an expected call of DeleteUTXO. -func (mr *MockDiffMockRecorder) DeleteUTXO(utxoID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) DeleteUTXO(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUTXO", reflect.TypeOf((*MockDiff)(nil).DeleteUTXO), utxoID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUTXO", reflect.TypeOf((*MockDiff)(nil).DeleteUTXO), arg0) } // GetChains mocks base method. -func (m *MockDiff) GetChains(subnetID ids.ID) ([]*txs.Tx, error) { +func (m *MockDiff) GetChains(arg0 ids.ID) ([]*txs.Tx, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetChains", subnetID) + ret := m.ctrl.Call(m, "GetChains", arg0) ret0, _ := ret[0].([]*txs.Tx) ret1, _ := ret[1].(error) return ret0, ret1 } // GetChains indicates an expected call of GetChains. -func (mr *MockDiffMockRecorder) GetChains(subnetID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetChains(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChains", reflect.TypeOf((*MockDiff)(nil).GetChains), subnetID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChains", reflect.TypeOf((*MockDiff)(nil).GetChains), arg0) } // GetCurrentDelegatorIterator mocks base method. -func (m *MockDiff) GetCurrentDelegatorIterator(subnetID ids.ID, nodeID ids.NodeID) (StakerIterator, error) { +func (m *MockDiff) GetCurrentDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentDelegatorIterator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetCurrentDelegatorIterator", arg0, arg1) ret0, _ := ret[0].(StakerIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentDelegatorIterator indicates an expected call of GetCurrentDelegatorIterator. -func (mr *MockDiffMockRecorder) GetCurrentDelegatorIterator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetCurrentDelegatorIterator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentDelegatorIterator", reflect.TypeOf((*MockDiff)(nil).GetCurrentDelegatorIterator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentDelegatorIterator", reflect.TypeOf((*MockDiff)(nil).GetCurrentDelegatorIterator), arg0, arg1) } // GetCurrentStakerIterator mocks base method. @@ -220,47 +232,48 @@ func (mr *MockDiffMockRecorder) GetCurrentStakerIterator() *gomock.Call { } // GetCurrentSupply mocks base method. -func (m *MockDiff) GetCurrentSupply() uint64 { +func (m *MockDiff) GetCurrentSupply(arg0 ids.ID) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentSupply") + ret := m.ctrl.Call(m, "GetCurrentSupply", arg0) ret0, _ := ret[0].(uint64) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // GetCurrentSupply indicates an expected call of GetCurrentSupply. -func (mr *MockDiffMockRecorder) GetCurrentSupply() *gomock.Call { +func (mr *MockDiffMockRecorder) GetCurrentSupply(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSupply", reflect.TypeOf((*MockDiff)(nil).GetCurrentSupply)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSupply", reflect.TypeOf((*MockDiff)(nil).GetCurrentSupply), arg0) } // GetCurrentValidator mocks base method. -func (m *MockDiff) GetCurrentValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { +func (m *MockDiff) GetCurrentValidator(arg0 ids.ID, arg1 ids.NodeID) (*Staker, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentValidator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetCurrentValidator", arg0, arg1) ret0, _ := ret[0].(*Staker) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentValidator indicates an expected call of GetCurrentValidator. -func (mr *MockDiffMockRecorder) GetCurrentValidator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetCurrentValidator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidator", reflect.TypeOf((*MockDiff)(nil).GetCurrentValidator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidator", reflect.TypeOf((*MockDiff)(nil).GetCurrentValidator), arg0, arg1) } // GetPendingDelegatorIterator mocks base method. -func (m *MockDiff) GetPendingDelegatorIterator(subnetID ids.ID, nodeID ids.NodeID) (StakerIterator, error) { +func (m *MockDiff) GetPendingDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingDelegatorIterator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetPendingDelegatorIterator", arg0, arg1) ret0, _ := ret[0].(StakerIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPendingDelegatorIterator indicates an expected call of GetPendingDelegatorIterator. -func (mr *MockDiffMockRecorder) GetPendingDelegatorIterator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetPendingDelegatorIterator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDelegatorIterator", reflect.TypeOf((*MockDiff)(nil).GetPendingDelegatorIterator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDelegatorIterator", reflect.TypeOf((*MockDiff)(nil).GetPendingDelegatorIterator), arg0, arg1) } // GetPendingStakerIterator mocks base method. @@ -279,33 +292,48 @@ func (mr *MockDiffMockRecorder) GetPendingStakerIterator() *gomock.Call { } // GetPendingValidator mocks base method. -func (m *MockDiff) GetPendingValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { +func (m *MockDiff) GetPendingValidator(arg0 ids.ID, arg1 ids.NodeID) (*Staker, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingValidator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetPendingValidator", arg0, arg1) ret0, _ := ret[0].(*Staker) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPendingValidator indicates an expected call of GetPendingValidator. -func (mr *MockDiffMockRecorder) GetPendingValidator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetPendingValidator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockDiff)(nil).GetPendingValidator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockDiff)(nil).GetPendingValidator), arg0, arg1) } // GetRewardUTXOs mocks base method. -func (m *MockDiff) GetRewardUTXOs(txID ids.ID) ([]*avax.UTXO, error) { +func (m *MockDiff) GetRewardUTXOs(arg0 ids.ID) ([]*avax.UTXO, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRewardUTXOs", txID) + ret := m.ctrl.Call(m, "GetRewardUTXOs", arg0) ret0, _ := ret[0].([]*avax.UTXO) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRewardUTXOs indicates an expected call of GetRewardUTXOs. -func (mr *MockDiffMockRecorder) GetRewardUTXOs(txID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetRewardUTXOs(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRewardUTXOs", reflect.TypeOf((*MockDiff)(nil).GetRewardUTXOs), arg0) +} + +// GetSubnetTransformation mocks base method. +func (m *MockDiff) GetSubnetTransformation(arg0 ids.ID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSubnetTransformation", arg0) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSubnetTransformation indicates an expected call of GetSubnetTransformation. +func (mr *MockDiffMockRecorder) GetSubnetTransformation(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRewardUTXOs", reflect.TypeOf((*MockDiff)(nil).GetRewardUTXOs), txID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetTransformation", reflect.TypeOf((*MockDiff)(nil).GetSubnetTransformation), arg0) } // GetSubnets mocks base method. @@ -338,9 +366,9 @@ func (mr *MockDiffMockRecorder) GetTimestamp() *gomock.Call { } // GetTx mocks base method. -func (m *MockDiff) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { +func (m *MockDiff) GetTx(arg0 ids.ID) (*txs.Tx, status.Status, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTx", txID) + ret := m.ctrl.Call(m, "GetTx", arg0) ret0, _ := ret[0].(*txs.Tx) ret1, _ := ret[1].(status.Status) ret2, _ := ret[2].(error) @@ -348,94 +376,94 @@ func (m *MockDiff) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { } // GetTx indicates an expected call of GetTx. -func (mr *MockDiffMockRecorder) GetTx(txID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetTx(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTx", reflect.TypeOf((*MockDiff)(nil).GetTx), txID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTx", reflect.TypeOf((*MockDiff)(nil).GetTx), arg0) } // GetUTXO mocks base method. -func (m *MockDiff) GetUTXO(utxoID ids.ID) (*avax.UTXO, error) { +func (m *MockDiff) GetUTXO(arg0 ids.ID) (*avax.UTXO, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUTXO", utxoID) + ret := m.ctrl.Call(m, "GetUTXO", arg0) ret0, _ := ret[0].(*avax.UTXO) ret1, _ := ret[1].(error) return ret0, ret1 } // GetUTXO indicates an expected call of GetUTXO. -func (mr *MockDiffMockRecorder) GetUTXO(utxoID interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockDiff)(nil).GetUTXO), utxoID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockDiff)(nil).GetUTXO), arg0) } // PutCurrentDelegator mocks base method. -func (m *MockDiff) PutCurrentDelegator(staker *Staker) { +func (m *MockDiff) PutCurrentDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutCurrentDelegator", staker) + m.ctrl.Call(m, "PutCurrentDelegator", arg0) } // PutCurrentDelegator indicates an expected call of PutCurrentDelegator. -func (mr *MockDiffMockRecorder) PutCurrentDelegator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) PutCurrentDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentDelegator", reflect.TypeOf((*MockDiff)(nil).PutCurrentDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentDelegator", reflect.TypeOf((*MockDiff)(nil).PutCurrentDelegator), arg0) } // PutCurrentValidator mocks base method. -func (m *MockDiff) PutCurrentValidator(staker *Staker) { +func (m *MockDiff) PutCurrentValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutCurrentValidator", staker) + m.ctrl.Call(m, "PutCurrentValidator", arg0) } // PutCurrentValidator indicates an expected call of PutCurrentValidator. -func (mr *MockDiffMockRecorder) PutCurrentValidator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) PutCurrentValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentValidator", reflect.TypeOf((*MockDiff)(nil).PutCurrentValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentValidator", reflect.TypeOf((*MockDiff)(nil).PutCurrentValidator), arg0) } // PutPendingDelegator mocks base method. -func (m *MockDiff) PutPendingDelegator(staker *Staker) { +func (m *MockDiff) PutPendingDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutPendingDelegator", staker) + m.ctrl.Call(m, "PutPendingDelegator", arg0) } // PutPendingDelegator indicates an expected call of PutPendingDelegator. -func (mr *MockDiffMockRecorder) PutPendingDelegator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) PutPendingDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingDelegator", reflect.TypeOf((*MockDiff)(nil).PutPendingDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingDelegator", reflect.TypeOf((*MockDiff)(nil).PutPendingDelegator), arg0) } // PutPendingValidator mocks base method. -func (m *MockDiff) PutPendingValidator(staker *Staker) { +func (m *MockDiff) PutPendingValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutPendingValidator", staker) + m.ctrl.Call(m, "PutPendingValidator", arg0) } // PutPendingValidator indicates an expected call of PutPendingValidator. -func (mr *MockDiffMockRecorder) PutPendingValidator(staker interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) PutPendingValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingValidator", reflect.TypeOf((*MockDiff)(nil).PutPendingValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingValidator", reflect.TypeOf((*MockDiff)(nil).PutPendingValidator), arg0) } // SetCurrentSupply mocks base method. -func (m *MockDiff) SetCurrentSupply(cs uint64) { +func (m *MockDiff) SetCurrentSupply(arg0 ids.ID, arg1 uint64) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetCurrentSupply", cs) + m.ctrl.Call(m, "SetCurrentSupply", arg0, arg1) } // SetCurrentSupply indicates an expected call of SetCurrentSupply. -func (mr *MockDiffMockRecorder) SetCurrentSupply(cs interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) SetCurrentSupply(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentSupply", reflect.TypeOf((*MockDiff)(nil).SetCurrentSupply), cs) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentSupply", reflect.TypeOf((*MockDiff)(nil).SetCurrentSupply), arg0, arg1) } // SetTimestamp mocks base method. -func (m *MockDiff) SetTimestamp(tm time.Time) { +func (m *MockDiff) SetTimestamp(arg0 time.Time) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetTimestamp", tm) + m.ctrl.Call(m, "SetTimestamp", arg0) } // SetTimestamp indicates an expected call of SetTimestamp. -func (mr *MockDiffMockRecorder) SetTimestamp(tm interface{}) *gomock.Call { +func (mr *MockDiffMockRecorder) SetTimestamp(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimestamp", reflect.TypeOf((*MockDiff)(nil).SetTimestamp), tm) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimestamp", reflect.TypeOf((*MockDiff)(nil).SetTimestamp), arg0) } diff --git a/avalanchego/vms/platformvm/state/mock_staker_iterator.go b/avalanchego/vms/platformvm/state/mock_staker_iterator.go index a6d1db86..e6da6f11 100644 --- a/avalanchego/vms/platformvm/state/mock_staker_iterator.go +++ b/avalanchego/vms/platformvm/state/mock_staker_iterator.go @@ -5,34 +5,35 @@ package state import ( - gomock "github.com/golang/mock/gomock" reflect "reflect" + + gomock "github.com/golang/mock/gomock" ) -// MockStakerIterator is a mock of StakerIterator interface +// MockStakerIterator is a mock of StakerIterator interface. type MockStakerIterator struct { ctrl *gomock.Controller recorder *MockStakerIteratorMockRecorder } -// MockStakerIteratorMockRecorder is the mock recorder for MockStakerIterator +// MockStakerIteratorMockRecorder is the mock recorder for MockStakerIterator. type MockStakerIteratorMockRecorder struct { mock *MockStakerIterator } -// NewMockStakerIterator creates a new mock instance +// NewMockStakerIterator creates a new mock instance. func NewMockStakerIterator(ctrl *gomock.Controller) *MockStakerIterator { mock := &MockStakerIterator{ctrl: ctrl} mock.recorder = &MockStakerIteratorMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStakerIterator) EXPECT() *MockStakerIteratorMockRecorder { return m.recorder } -// Next mocks base method +// Next mocks base method. func (m *MockStakerIterator) Next() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Next") @@ -40,25 +41,25 @@ func (m *MockStakerIterator) Next() bool { return ret0 } -// Next indicates an expected call of Next +// Next indicates an expected call of Next. func (mr *MockStakerIteratorMockRecorder) Next() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockStakerIterator)(nil).Next)) } -// Release mocks base method +// Release mocks base method. func (m *MockStakerIterator) Release() { m.ctrl.T.Helper() m.ctrl.Call(m, "Release") } -// Release indicates an expected call of Release +// Release indicates an expected call of Release. func (mr *MockStakerIteratorMockRecorder) Release() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Release", reflect.TypeOf((*MockStakerIterator)(nil).Release)) } -// Value mocks base method +// Value mocks base method. func (m *MockStakerIterator) Value() *Staker { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Value") @@ -66,7 +67,7 @@ func (m *MockStakerIterator) Value() *Staker { return ret0 } -// Value indicates an expected call of Value +// Value indicates an expected call of Value. func (mr *MockStakerIteratorMockRecorder) Value() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Value", reflect.TypeOf((*MockStakerIterator)(nil).Value)) diff --git a/avalanchego/vms/platformvm/state/mock_state.go b/avalanchego/vms/platformvm/state/mock_state.go index e1cf5123..23f833b1 100644 --- a/avalanchego/vms/platformvm/state/mock_state.go +++ b/avalanchego/vms/platformvm/state/mock_state.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: vms/platformvm/state/state.go +// Source: github.com/ava-labs/avalanchego/vms/platformvm/state (interfaces: State) // Package state is a generated GoMock package. package state @@ -19,515 +19,6 @@ import ( gomock "github.com/golang/mock/gomock" ) -// MockChain is a mock of Chain interface. -type MockChain struct { - ctrl *gomock.Controller - recorder *MockChainMockRecorder -} - -// MockChainMockRecorder is the mock recorder for MockChain. -type MockChainMockRecorder struct { - mock *MockChain -} - -// NewMockChain creates a new mock instance. -func NewMockChain(ctrl *gomock.Controller) *MockChain { - mock := &MockChain{ctrl: ctrl} - mock.recorder = &MockChainMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockChain) EXPECT() *MockChainMockRecorder { - return m.recorder -} - -// AddChain mocks base method. -func (m *MockChain) AddChain(createChainTx *txs.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddChain", createChainTx) -} - -// AddChain indicates an expected call of AddChain. -func (mr *MockChainMockRecorder) AddChain(createChainTx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChain", reflect.TypeOf((*MockChain)(nil).AddChain), createChainTx) -} - -// AddRewardUTXO mocks base method. -func (m *MockChain) AddRewardUTXO(txID ids.ID, utxo *avax.UTXO) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddRewardUTXO", txID, utxo) -} - -// AddRewardUTXO indicates an expected call of AddRewardUTXO. -func (mr *MockChainMockRecorder) AddRewardUTXO(txID, utxo interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewardUTXO", reflect.TypeOf((*MockChain)(nil).AddRewardUTXO), txID, utxo) -} - -// AddSubnet mocks base method. -func (m *MockChain) AddSubnet(createSubnetTx *txs.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddSubnet", createSubnetTx) -} - -// AddSubnet indicates an expected call of AddSubnet. -func (mr *MockChainMockRecorder) AddSubnet(createSubnetTx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnet", reflect.TypeOf((*MockChain)(nil).AddSubnet), createSubnetTx) -} - -// AddTx mocks base method. -func (m *MockChain) AddTx(tx *txs.Tx, status status.Status) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddTx", tx, status) -} - -// AddTx indicates an expected call of AddTx. -func (mr *MockChainMockRecorder) AddTx(tx, status interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTx", reflect.TypeOf((*MockChain)(nil).AddTx), tx, status) -} - -// AddUTXO mocks base method. -func (m *MockChain) AddUTXO(utxo *avax.UTXO) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddUTXO", utxo) -} - -// AddUTXO indicates an expected call of AddUTXO. -func (mr *MockChainMockRecorder) AddUTXO(utxo interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUTXO", reflect.TypeOf((*MockChain)(nil).AddUTXO), utxo) -} - -// DeleteCurrentDelegator mocks base method. -func (m *MockChain) DeleteCurrentDelegator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteCurrentDelegator", staker) -} - -// DeleteCurrentDelegator indicates an expected call of DeleteCurrentDelegator. -func (mr *MockChainMockRecorder) DeleteCurrentDelegator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentDelegator", reflect.TypeOf((*MockChain)(nil).DeleteCurrentDelegator), staker) -} - -// DeleteCurrentValidator mocks base method. -func (m *MockChain) DeleteCurrentValidator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteCurrentValidator", staker) -} - -// DeleteCurrentValidator indicates an expected call of DeleteCurrentValidator. -func (mr *MockChainMockRecorder) DeleteCurrentValidator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentValidator", reflect.TypeOf((*MockChain)(nil).DeleteCurrentValidator), staker) -} - -// DeletePendingDelegator mocks base method. -func (m *MockChain) DeletePendingDelegator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DeletePendingDelegator", staker) -} - -// DeletePendingDelegator indicates an expected call of DeletePendingDelegator. -func (mr *MockChainMockRecorder) DeletePendingDelegator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingDelegator", reflect.TypeOf((*MockChain)(nil).DeletePendingDelegator), staker) -} - -// DeletePendingValidator mocks base method. -func (m *MockChain) DeletePendingValidator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DeletePendingValidator", staker) -} - -// DeletePendingValidator indicates an expected call of DeletePendingValidator. -func (mr *MockChainMockRecorder) DeletePendingValidator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingValidator", reflect.TypeOf((*MockChain)(nil).DeletePendingValidator), staker) -} - -// DeleteUTXO mocks base method. -func (m *MockChain) DeleteUTXO(utxoID ids.ID) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteUTXO", utxoID) -} - -// DeleteUTXO indicates an expected call of DeleteUTXO. -func (mr *MockChainMockRecorder) DeleteUTXO(utxoID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUTXO", reflect.TypeOf((*MockChain)(nil).DeleteUTXO), utxoID) -} - -// GetChains mocks base method. -func (m *MockChain) GetChains(subnetID ids.ID) ([]*txs.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetChains", subnetID) - ret0, _ := ret[0].([]*txs.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetChains indicates an expected call of GetChains. -func (mr *MockChainMockRecorder) GetChains(subnetID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChains", reflect.TypeOf((*MockChain)(nil).GetChains), subnetID) -} - -// GetCurrentDelegatorIterator mocks base method. -func (m *MockChain) GetCurrentDelegatorIterator(subnetID ids.ID, nodeID ids.NodeID) (StakerIterator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentDelegatorIterator", subnetID, nodeID) - ret0, _ := ret[0].(StakerIterator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetCurrentDelegatorIterator indicates an expected call of GetCurrentDelegatorIterator. -func (mr *MockChainMockRecorder) GetCurrentDelegatorIterator(subnetID, nodeID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentDelegatorIterator", reflect.TypeOf((*MockChain)(nil).GetCurrentDelegatorIterator), subnetID, nodeID) -} - -// GetCurrentStakerIterator mocks base method. -func (m *MockChain) GetCurrentStakerIterator() (StakerIterator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentStakerIterator") - ret0, _ := ret[0].(StakerIterator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetCurrentStakerIterator indicates an expected call of GetCurrentStakerIterator. -func (mr *MockChainMockRecorder) GetCurrentStakerIterator() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentStakerIterator", reflect.TypeOf((*MockChain)(nil).GetCurrentStakerIterator)) -} - -// GetCurrentSupply mocks base method. -func (m *MockChain) GetCurrentSupply() uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentSupply") - ret0, _ := ret[0].(uint64) - return ret0 -} - -// GetCurrentSupply indicates an expected call of GetCurrentSupply. -func (mr *MockChainMockRecorder) GetCurrentSupply() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSupply", reflect.TypeOf((*MockChain)(nil).GetCurrentSupply)) -} - -// GetCurrentValidator mocks base method. -func (m *MockChain) GetCurrentValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentValidator", subnetID, nodeID) - ret0, _ := ret[0].(*Staker) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetCurrentValidator indicates an expected call of GetCurrentValidator. -func (mr *MockChainMockRecorder) GetCurrentValidator(subnetID, nodeID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidator", reflect.TypeOf((*MockChain)(nil).GetCurrentValidator), subnetID, nodeID) -} - -// GetPendingDelegatorIterator mocks base method. -func (m *MockChain) GetPendingDelegatorIterator(subnetID ids.ID, nodeID ids.NodeID) (StakerIterator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingDelegatorIterator", subnetID, nodeID) - ret0, _ := ret[0].(StakerIterator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPendingDelegatorIterator indicates an expected call of GetPendingDelegatorIterator. -func (mr *MockChainMockRecorder) GetPendingDelegatorIterator(subnetID, nodeID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDelegatorIterator", reflect.TypeOf((*MockChain)(nil).GetPendingDelegatorIterator), subnetID, nodeID) -} - -// GetPendingStakerIterator mocks base method. -func (m *MockChain) GetPendingStakerIterator() (StakerIterator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingStakerIterator") - ret0, _ := ret[0].(StakerIterator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPendingStakerIterator indicates an expected call of GetPendingStakerIterator. -func (mr *MockChainMockRecorder) GetPendingStakerIterator() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingStakerIterator", reflect.TypeOf((*MockChain)(nil).GetPendingStakerIterator)) -} - -// GetPendingValidator mocks base method. -func (m *MockChain) GetPendingValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingValidator", subnetID, nodeID) - ret0, _ := ret[0].(*Staker) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPendingValidator indicates an expected call of GetPendingValidator. -func (mr *MockChainMockRecorder) GetPendingValidator(subnetID, nodeID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockChain)(nil).GetPendingValidator), subnetID, nodeID) -} - -// GetRewardUTXOs mocks base method. -func (m *MockChain) GetRewardUTXOs(txID ids.ID) ([]*avax.UTXO, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRewardUTXOs", txID) - ret0, _ := ret[0].([]*avax.UTXO) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetRewardUTXOs indicates an expected call of GetRewardUTXOs. -func (mr *MockChainMockRecorder) GetRewardUTXOs(txID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRewardUTXOs", reflect.TypeOf((*MockChain)(nil).GetRewardUTXOs), txID) -} - -// GetSubnets mocks base method. -func (m *MockChain) GetSubnets() ([]*txs.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSubnets") - ret0, _ := ret[0].([]*txs.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSubnets indicates an expected call of GetSubnets. -func (mr *MockChainMockRecorder) GetSubnets() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnets", reflect.TypeOf((*MockChain)(nil).GetSubnets)) -} - -// GetTimestamp mocks base method. -func (m *MockChain) GetTimestamp() time.Time { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTimestamp") - ret0, _ := ret[0].(time.Time) - return ret0 -} - -// GetTimestamp indicates an expected call of GetTimestamp. -func (mr *MockChainMockRecorder) GetTimestamp() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimestamp", reflect.TypeOf((*MockChain)(nil).GetTimestamp)) -} - -// GetTx mocks base method. -func (m *MockChain) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTx", txID) - ret0, _ := ret[0].(*txs.Tx) - ret1, _ := ret[1].(status.Status) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetTx indicates an expected call of GetTx. -func (mr *MockChainMockRecorder) GetTx(txID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTx", reflect.TypeOf((*MockChain)(nil).GetTx), txID) -} - -// GetUTXO mocks base method. -func (m *MockChain) GetUTXO(utxoID ids.ID) (*avax.UTXO, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUTXO", utxoID) - ret0, _ := ret[0].(*avax.UTXO) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetUTXO indicates an expected call of GetUTXO. -func (mr *MockChainMockRecorder) GetUTXO(utxoID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockChain)(nil).GetUTXO), utxoID) -} - -// PutCurrentDelegator mocks base method. -func (m *MockChain) PutCurrentDelegator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "PutCurrentDelegator", staker) -} - -// PutCurrentDelegator indicates an expected call of PutCurrentDelegator. -func (mr *MockChainMockRecorder) PutCurrentDelegator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentDelegator", reflect.TypeOf((*MockChain)(nil).PutCurrentDelegator), staker) -} - -// PutCurrentValidator mocks base method. -func (m *MockChain) PutCurrentValidator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "PutCurrentValidator", staker) -} - -// PutCurrentValidator indicates an expected call of PutCurrentValidator. -func (mr *MockChainMockRecorder) PutCurrentValidator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentValidator", reflect.TypeOf((*MockChain)(nil).PutCurrentValidator), staker) -} - -// PutPendingDelegator mocks base method. -func (m *MockChain) PutPendingDelegator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "PutPendingDelegator", staker) -} - -// PutPendingDelegator indicates an expected call of PutPendingDelegator. -func (mr *MockChainMockRecorder) PutPendingDelegator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingDelegator", reflect.TypeOf((*MockChain)(nil).PutPendingDelegator), staker) -} - -// PutPendingValidator mocks base method. -func (m *MockChain) PutPendingValidator(staker *Staker) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "PutPendingValidator", staker) -} - -// PutPendingValidator indicates an expected call of PutPendingValidator. -func (mr *MockChainMockRecorder) PutPendingValidator(staker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingValidator", reflect.TypeOf((*MockChain)(nil).PutPendingValidator), staker) -} - -// SetCurrentSupply mocks base method. -func (m *MockChain) SetCurrentSupply(cs uint64) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetCurrentSupply", cs) -} - -// SetCurrentSupply indicates an expected call of SetCurrentSupply. -func (mr *MockChainMockRecorder) SetCurrentSupply(cs interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentSupply", reflect.TypeOf((*MockChain)(nil).SetCurrentSupply), cs) -} - -// SetTimestamp mocks base method. -func (m *MockChain) SetTimestamp(tm time.Time) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetTimestamp", tm) -} - -// SetTimestamp indicates an expected call of SetTimestamp. -func (mr *MockChainMockRecorder) SetTimestamp(tm interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimestamp", reflect.TypeOf((*MockChain)(nil).SetTimestamp), tm) -} - -// MockLastAccepteder is a mock of LastAccepteder interface. -type MockLastAccepteder struct { - ctrl *gomock.Controller - recorder *MockLastAcceptederMockRecorder -} - -// MockLastAcceptederMockRecorder is the mock recorder for MockLastAccepteder. -type MockLastAcceptederMockRecorder struct { - mock *MockLastAccepteder -} - -// NewMockLastAccepteder creates a new mock instance. -func NewMockLastAccepteder(ctrl *gomock.Controller) *MockLastAccepteder { - mock := &MockLastAccepteder{ctrl: ctrl} - mock.recorder = &MockLastAcceptederMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLastAccepteder) EXPECT() *MockLastAcceptederMockRecorder { - return m.recorder -} - -// GetLastAccepted mocks base method. -func (m *MockLastAccepteder) GetLastAccepted() ids.ID { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLastAccepted") - ret0, _ := ret[0].(ids.ID) - return ret0 -} - -// GetLastAccepted indicates an expected call of GetLastAccepted. -func (mr *MockLastAcceptederMockRecorder) GetLastAccepted() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastAccepted", reflect.TypeOf((*MockLastAccepteder)(nil).GetLastAccepted)) -} - -// SetLastAccepted mocks base method. -func (m *MockLastAccepteder) SetLastAccepted(blkID ids.ID) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetLastAccepted", blkID) -} - -// SetLastAccepted indicates an expected call of SetLastAccepted. -func (mr *MockLastAcceptederMockRecorder) SetLastAccepted(blkID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastAccepted", reflect.TypeOf((*MockLastAccepteder)(nil).SetLastAccepted), blkID) -} - -// MockBlockState is a mock of BlockState interface. -type MockBlockState struct { - ctrl *gomock.Controller - recorder *MockBlockStateMockRecorder -} - -// MockBlockStateMockRecorder is the mock recorder for MockBlockState. -type MockBlockStateMockRecorder struct { - mock *MockBlockState -} - -// NewMockBlockState creates a new mock instance. -func NewMockBlockState(ctrl *gomock.Controller) *MockBlockState { - mock := &MockBlockState{ctrl: ctrl} - mock.recorder = &MockBlockStateMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBlockState) EXPECT() *MockBlockStateMockRecorder { - return m.recorder -} - -// AddStatelessBlock mocks base method. -func (m *MockBlockState) AddStatelessBlock(block blocks.Block, status choices.Status) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddStatelessBlock", block, status) -} - -// AddStatelessBlock indicates an expected call of AddStatelessBlock. -func (mr *MockBlockStateMockRecorder) AddStatelessBlock(block, status interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddStatelessBlock", reflect.TypeOf((*MockBlockState)(nil).AddStatelessBlock), block, status) -} - -// GetStatelessBlock mocks base method. -func (m *MockBlockState) GetStatelessBlock(blockID ids.ID) (blocks.Block, choices.Status, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStatelessBlock", blockID) - ret0, _ := ret[0].(blocks.Block) - ret1, _ := ret[1].(choices.Status) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetStatelessBlock indicates an expected call of GetStatelessBlock. -func (mr *MockBlockStateMockRecorder) GetStatelessBlock(blockID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessBlock", reflect.TypeOf((*MockBlockState)(nil).GetStatelessBlock), blockID) -} - // MockState is a mock of State interface. type MockState struct { ctrl *gomock.Controller @@ -568,75 +59,87 @@ func (m *MockState) GetNetworkID() uint32 { } // AddChain mocks base method. -func (m *MockState) AddChain(createChainTx *txs.Tx) { +func (m *MockState) AddChain(arg0 *txs.Tx) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddChain", createChainTx) + m.ctrl.Call(m, "AddChain", arg0) } // AddChain indicates an expected call of AddChain. -func (mr *MockStateMockRecorder) AddChain(createChainTx interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) AddChain(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChain", reflect.TypeOf((*MockState)(nil).AddChain), createChainTx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChain", reflect.TypeOf((*MockState)(nil).AddChain), arg0) } // AddRewardUTXO mocks base method. -func (m *MockState) AddRewardUTXO(txID ids.ID, utxo *avax.UTXO) { +func (m *MockState) AddRewardUTXO(arg0 ids.ID, arg1 *avax.UTXO) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddRewardUTXO", txID, utxo) + m.ctrl.Call(m, "AddRewardUTXO", arg0, arg1) } // AddRewardUTXO indicates an expected call of AddRewardUTXO. -func (mr *MockStateMockRecorder) AddRewardUTXO(txID, utxo interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) AddRewardUTXO(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewardUTXO", reflect.TypeOf((*MockState)(nil).AddRewardUTXO), txID, utxo) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewardUTXO", reflect.TypeOf((*MockState)(nil).AddRewardUTXO), arg0, arg1) } // AddStatelessBlock mocks base method. -func (m *MockState) AddStatelessBlock(block blocks.Block, status choices.Status) { +func (m *MockState) AddStatelessBlock(arg0 blocks.Block, arg1 choices.Status) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddStatelessBlock", block, status) + m.ctrl.Call(m, "AddStatelessBlock", arg0, arg1) } // AddStatelessBlock indicates an expected call of AddStatelessBlock. -func (mr *MockStateMockRecorder) AddStatelessBlock(block, status interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) AddStatelessBlock(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddStatelessBlock", reflect.TypeOf((*MockState)(nil).AddStatelessBlock), block, status) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddStatelessBlock", reflect.TypeOf((*MockState)(nil).AddStatelessBlock), arg0, arg1) } // AddSubnet mocks base method. -func (m *MockState) AddSubnet(createSubnetTx *txs.Tx) { +func (m *MockState) AddSubnet(arg0 *txs.Tx) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddSubnet", createSubnetTx) + m.ctrl.Call(m, "AddSubnet", arg0) } // AddSubnet indicates an expected call of AddSubnet. -func (mr *MockStateMockRecorder) AddSubnet(createSubnetTx interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) AddSubnet(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnet", reflect.TypeOf((*MockState)(nil).AddSubnet), arg0) +} + +// AddSubnetTransformation mocks base method. +func (m *MockState) AddSubnetTransformation(arg0 *txs.Tx) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddSubnetTransformation", arg0) +} + +// AddSubnetTransformation indicates an expected call of AddSubnetTransformation. +func (mr *MockStateMockRecorder) AddSubnetTransformation(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnet", reflect.TypeOf((*MockState)(nil).AddSubnet), createSubnetTx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubnetTransformation", reflect.TypeOf((*MockState)(nil).AddSubnetTransformation), arg0) } // AddTx mocks base method. -func (m *MockState) AddTx(tx *txs.Tx, status status.Status) { +func (m *MockState) AddTx(arg0 *txs.Tx, arg1 status.Status) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddTx", tx, status) + m.ctrl.Call(m, "AddTx", arg0, arg1) } // AddTx indicates an expected call of AddTx. -func (mr *MockStateMockRecorder) AddTx(tx, status interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) AddTx(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTx", reflect.TypeOf((*MockState)(nil).AddTx), tx, status) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTx", reflect.TypeOf((*MockState)(nil).AddTx), arg0, arg1) } // AddUTXO mocks base method. -func (m *MockState) AddUTXO(utxo *avax.UTXO) { +func (m *MockState) AddUTXO(arg0 *avax.UTXO) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddUTXO", utxo) + m.ctrl.Call(m, "AddUTXO", arg0) } // AddUTXO indicates an expected call of AddUTXO. -func (mr *MockStateMockRecorder) AddUTXO(utxo interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) AddUTXO(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUTXO", reflect.TypeOf((*MockState)(nil).AddUTXO), utxo) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUTXO", reflect.TypeOf((*MockState)(nil).AddUTXO), arg0) } // Close mocks base method. @@ -683,93 +186,93 @@ func (mr *MockStateMockRecorder) CommitBatch() *gomock.Call { } // DeleteCurrentDelegator mocks base method. -func (m *MockState) DeleteCurrentDelegator(staker *Staker) { +func (m *MockState) DeleteCurrentDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteCurrentDelegator", staker) + m.ctrl.Call(m, "DeleteCurrentDelegator", arg0) } // DeleteCurrentDelegator indicates an expected call of DeleteCurrentDelegator. -func (mr *MockStateMockRecorder) DeleteCurrentDelegator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) DeleteCurrentDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentDelegator", reflect.TypeOf((*MockState)(nil).DeleteCurrentDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentDelegator", reflect.TypeOf((*MockState)(nil).DeleteCurrentDelegator), arg0) } // DeleteCurrentValidator mocks base method. -func (m *MockState) DeleteCurrentValidator(staker *Staker) { +func (m *MockState) DeleteCurrentValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteCurrentValidator", staker) + m.ctrl.Call(m, "DeleteCurrentValidator", arg0) } // DeleteCurrentValidator indicates an expected call of DeleteCurrentValidator. -func (mr *MockStateMockRecorder) DeleteCurrentValidator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) DeleteCurrentValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentValidator", reflect.TypeOf((*MockState)(nil).DeleteCurrentValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentValidator", reflect.TypeOf((*MockState)(nil).DeleteCurrentValidator), arg0) } // DeletePendingDelegator mocks base method. -func (m *MockState) DeletePendingDelegator(staker *Staker) { +func (m *MockState) DeletePendingDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeletePendingDelegator", staker) + m.ctrl.Call(m, "DeletePendingDelegator", arg0) } // DeletePendingDelegator indicates an expected call of DeletePendingDelegator. -func (mr *MockStateMockRecorder) DeletePendingDelegator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) DeletePendingDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingDelegator", reflect.TypeOf((*MockState)(nil).DeletePendingDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingDelegator", reflect.TypeOf((*MockState)(nil).DeletePendingDelegator), arg0) } // DeletePendingValidator mocks base method. -func (m *MockState) DeletePendingValidator(staker *Staker) { +func (m *MockState) DeletePendingValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeletePendingValidator", staker) + m.ctrl.Call(m, "DeletePendingValidator", arg0) } // DeletePendingValidator indicates an expected call of DeletePendingValidator. -func (mr *MockStateMockRecorder) DeletePendingValidator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) DeletePendingValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingValidator", reflect.TypeOf((*MockState)(nil).DeletePendingValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePendingValidator", reflect.TypeOf((*MockState)(nil).DeletePendingValidator), arg0) } // DeleteUTXO mocks base method. -func (m *MockState) DeleteUTXO(utxoID ids.ID) { +func (m *MockState) DeleteUTXO(arg0 ids.ID) { m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteUTXO", utxoID) + m.ctrl.Call(m, "DeleteUTXO", arg0) } // DeleteUTXO indicates an expected call of DeleteUTXO. -func (mr *MockStateMockRecorder) DeleteUTXO(utxoID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) DeleteUTXO(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUTXO", reflect.TypeOf((*MockState)(nil).DeleteUTXO), utxoID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUTXO", reflect.TypeOf((*MockState)(nil).DeleteUTXO), arg0) } // GetChains mocks base method. -func (m *MockState) GetChains(subnetID ids.ID) ([]*txs.Tx, error) { +func (m *MockState) GetChains(arg0 ids.ID) ([]*txs.Tx, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetChains", subnetID) + ret := m.ctrl.Call(m, "GetChains", arg0) ret0, _ := ret[0].([]*txs.Tx) ret1, _ := ret[1].(error) return ret0, ret1 } // GetChains indicates an expected call of GetChains. -func (mr *MockStateMockRecorder) GetChains(subnetID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetChains(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChains", reflect.TypeOf((*MockState)(nil).GetChains), subnetID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChains", reflect.TypeOf((*MockState)(nil).GetChains), arg0) } // GetCurrentDelegatorIterator mocks base method. -func (m *MockState) GetCurrentDelegatorIterator(subnetID ids.ID, nodeID ids.NodeID) (StakerIterator, error) { +func (m *MockState) GetCurrentDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentDelegatorIterator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetCurrentDelegatorIterator", arg0, arg1) ret0, _ := ret[0].(StakerIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentDelegatorIterator indicates an expected call of GetCurrentDelegatorIterator. -func (mr *MockStateMockRecorder) GetCurrentDelegatorIterator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetCurrentDelegatorIterator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentDelegatorIterator", reflect.TypeOf((*MockState)(nil).GetCurrentDelegatorIterator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentDelegatorIterator", reflect.TypeOf((*MockState)(nil).GetCurrentDelegatorIterator), arg0, arg1) } // GetCurrentStakerIterator mocks base method. @@ -788,32 +291,33 @@ func (mr *MockStateMockRecorder) GetCurrentStakerIterator() *gomock.Call { } // GetCurrentSupply mocks base method. -func (m *MockState) GetCurrentSupply() uint64 { +func (m *MockState) GetCurrentSupply(arg0 ids.ID) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentSupply") + ret := m.ctrl.Call(m, "GetCurrentSupply", arg0) ret0, _ := ret[0].(uint64) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // GetCurrentSupply indicates an expected call of GetCurrentSupply. -func (mr *MockStateMockRecorder) GetCurrentSupply() *gomock.Call { +func (mr *MockStateMockRecorder) GetCurrentSupply(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSupply", reflect.TypeOf((*MockState)(nil).GetCurrentSupply)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSupply", reflect.TypeOf((*MockState)(nil).GetCurrentSupply), arg0) } // GetCurrentValidator mocks base method. -func (m *MockState) GetCurrentValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { +func (m *MockState) GetCurrentValidator(arg0 ids.ID, arg1 ids.NodeID) (*Staker, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentValidator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetCurrentValidator", arg0, arg1) ret0, _ := ret[0].(*Staker) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentValidator indicates an expected call of GetCurrentValidator. -func (mr *MockStateMockRecorder) GetCurrentValidator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetCurrentValidator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidator", reflect.TypeOf((*MockState)(nil).GetCurrentValidator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidator", reflect.TypeOf((*MockState)(nil).GetCurrentValidator), arg0, arg1) } // GetLastAccepted mocks base method. @@ -830,19 +334,33 @@ func (mr *MockStateMockRecorder) GetLastAccepted() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastAccepted", reflect.TypeOf((*MockState)(nil).GetLastAccepted)) } +// GetNetworkID mocks base method. +func (m *MockChain) GetNetworkID() uint32 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetworkID") + ret0, _ := ret[0].(uint32) + return ret0 +} + +// GetNetworkID indicates an expected call of GetNetworkID. +func (mr *MockChainMockRecorder) GetNetworkID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkID", reflect.TypeOf((*MockChain)(nil).GetNetworkID)) +} + // GetPendingDelegatorIterator mocks base method. -func (m *MockState) GetPendingDelegatorIterator(subnetID ids.ID, nodeID ids.NodeID) (StakerIterator, error) { +func (m *MockState) GetPendingDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingDelegatorIterator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetPendingDelegatorIterator", arg0, arg1) ret0, _ := ret[0].(StakerIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPendingDelegatorIterator indicates an expected call of GetPendingDelegatorIterator. -func (mr *MockStateMockRecorder) GetPendingDelegatorIterator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetPendingDelegatorIterator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDelegatorIterator", reflect.TypeOf((*MockState)(nil).GetPendingDelegatorIterator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDelegatorIterator", reflect.TypeOf((*MockState)(nil).GetPendingDelegatorIterator), arg0, arg1) } // GetPendingStakerIterator mocks base method. @@ -861,54 +379,54 @@ func (mr *MockStateMockRecorder) GetPendingStakerIterator() *gomock.Call { } // GetPendingValidator mocks base method. -func (m *MockState) GetPendingValidator(subnetID ids.ID, nodeID ids.NodeID) (*Staker, error) { +func (m *MockState) GetPendingValidator(arg0 ids.ID, arg1 ids.NodeID) (*Staker, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingValidator", subnetID, nodeID) + ret := m.ctrl.Call(m, "GetPendingValidator", arg0, arg1) ret0, _ := ret[0].(*Staker) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPendingValidator indicates an expected call of GetPendingValidator. -func (mr *MockStateMockRecorder) GetPendingValidator(subnetID, nodeID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetPendingValidator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockState)(nil).GetPendingValidator), subnetID, nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingValidator", reflect.TypeOf((*MockState)(nil).GetPendingValidator), arg0, arg1) } // GetRewardUTXOs mocks base method. -func (m *MockState) GetRewardUTXOs(txID ids.ID) ([]*avax.UTXO, error) { +func (m *MockState) GetRewardUTXOs(arg0 ids.ID) ([]*avax.UTXO, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRewardUTXOs", txID) + ret := m.ctrl.Call(m, "GetRewardUTXOs", arg0) ret0, _ := ret[0].([]*avax.UTXO) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRewardUTXOs indicates an expected call of GetRewardUTXOs. -func (mr *MockStateMockRecorder) GetRewardUTXOs(txID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetRewardUTXOs(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRewardUTXOs", reflect.TypeOf((*MockState)(nil).GetRewardUTXOs), txID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRewardUTXOs", reflect.TypeOf((*MockState)(nil).GetRewardUTXOs), arg0) } // GetStartTime mocks base method. -func (m *MockState) GetStartTime(nodeID ids.NodeID) (time.Time, error) { +func (m *MockState) GetStartTime(arg0 ids.NodeID) (time.Time, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStartTime", nodeID) + ret := m.ctrl.Call(m, "GetStartTime", arg0) ret0, _ := ret[0].(time.Time) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStartTime indicates an expected call of GetStartTime. -func (mr *MockStateMockRecorder) GetStartTime(nodeID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetStartTime(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStartTime", reflect.TypeOf((*MockState)(nil).GetStartTime), nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStartTime", reflect.TypeOf((*MockState)(nil).GetStartTime), arg0) } // GetStatelessBlock mocks base method. -func (m *MockState) GetStatelessBlock(blockID ids.ID) (blocks.Block, choices.Status, error) { +func (m *MockState) GetStatelessBlock(arg0 ids.ID) (blocks.Block, choices.Status, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStatelessBlock", blockID) + ret := m.ctrl.Call(m, "GetStatelessBlock", arg0) ret0, _ := ret[0].(blocks.Block) ret1, _ := ret[1].(choices.Status) ret2, _ := ret[2].(error) @@ -916,9 +434,24 @@ func (m *MockState) GetStatelessBlock(blockID ids.ID) (blocks.Block, choices.Sta } // GetStatelessBlock indicates an expected call of GetStatelessBlock. -func (mr *MockStateMockRecorder) GetStatelessBlock(blockID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetStatelessBlock(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessBlock", reflect.TypeOf((*MockState)(nil).GetStatelessBlock), arg0) +} + +// GetSubnetTransformation mocks base method. +func (m *MockState) GetSubnetTransformation(arg0 ids.ID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSubnetTransformation", arg0) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSubnetTransformation indicates an expected call of GetSubnetTransformation. +func (mr *MockStateMockRecorder) GetSubnetTransformation(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessBlock", reflect.TypeOf((*MockState)(nil).GetStatelessBlock), blockID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetTransformation", reflect.TypeOf((*MockState)(nil).GetSubnetTransformation), arg0) } // GetSubnets mocks base method. @@ -951,9 +484,9 @@ func (mr *MockStateMockRecorder) GetTimestamp() *gomock.Call { } // GetTx mocks base method. -func (m *MockState) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { +func (m *MockState) GetTx(arg0 ids.ID) (*txs.Tx, status.Status, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTx", txID) + ret := m.ctrl.Call(m, "GetTx", arg0) ret0, _ := ret[0].(*txs.Tx) ret1, _ := ret[1].(status.Status) ret2, _ := ret[2].(error) @@ -961,30 +494,30 @@ func (m *MockState) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { } // GetTx indicates an expected call of GetTx. -func (mr *MockStateMockRecorder) GetTx(txID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetTx(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTx", reflect.TypeOf((*MockState)(nil).GetTx), txID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTx", reflect.TypeOf((*MockState)(nil).GetTx), arg0) } // GetUTXO mocks base method. -func (m *MockState) GetUTXO(utxoID ids.ID) (*avax.UTXO, error) { +func (m *MockState) GetUTXO(arg0 ids.ID) (*avax.UTXO, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUTXO", utxoID) + ret := m.ctrl.Call(m, "GetUTXO", arg0) ret0, _ := ret[0].(*avax.UTXO) ret1, _ := ret[1].(error) return ret0, ret1 } // GetUTXO indicates an expected call of GetUTXO. -func (mr *MockStateMockRecorder) GetUTXO(utxoID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockState)(nil).GetUTXO), utxoID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockState)(nil).GetUTXO), arg0) } // GetUptime mocks base method. -func (m *MockState) GetUptime(nodeID ids.NodeID) (time.Duration, time.Time, error) { +func (m *MockState) GetUptime(arg0 ids.NodeID) (time.Duration, time.Time, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUptime", nodeID) + ret := m.ctrl.Call(m, "GetUptime", arg0) ret0, _ := ret[0].(time.Duration) ret1, _ := ret[1].(time.Time) ret2, _ := ret[2].(error) @@ -992,162 +525,162 @@ func (m *MockState) GetUptime(nodeID ids.NodeID) (time.Duration, time.Time, erro } // GetUptime indicates an expected call of GetUptime. -func (mr *MockStateMockRecorder) GetUptime(nodeID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetUptime(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUptime", reflect.TypeOf((*MockState)(nil).GetUptime), nodeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUptime", reflect.TypeOf((*MockState)(nil).GetUptime), arg0) } // GetValidatorWeightDiffs mocks base method. -func (m *MockState) GetValidatorWeightDiffs(height uint64, subnetID ids.ID) (map[ids.NodeID]*ValidatorWeightDiff, error) { +func (m *MockState) GetValidatorWeightDiffs(arg0 uint64, arg1 ids.ID) (map[ids.NodeID]*ValidatorWeightDiff, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetValidatorWeightDiffs", height, subnetID) + ret := m.ctrl.Call(m, "GetValidatorWeightDiffs", arg0, arg1) ret0, _ := ret[0].(map[ids.NodeID]*ValidatorWeightDiff) ret1, _ := ret[1].(error) return ret0, ret1 } // GetValidatorWeightDiffs indicates an expected call of GetValidatorWeightDiffs. -func (mr *MockStateMockRecorder) GetValidatorWeightDiffs(height, subnetID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) GetValidatorWeightDiffs(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorWeightDiffs", reflect.TypeOf((*MockState)(nil).GetValidatorWeightDiffs), height, subnetID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorWeightDiffs", reflect.TypeOf((*MockState)(nil).GetValidatorWeightDiffs), arg0, arg1) } // PutCurrentDelegator mocks base method. -func (m *MockState) PutCurrentDelegator(staker *Staker) { +func (m *MockState) PutCurrentDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutCurrentDelegator", staker) + m.ctrl.Call(m, "PutCurrentDelegator", arg0) } // PutCurrentDelegator indicates an expected call of PutCurrentDelegator. -func (mr *MockStateMockRecorder) PutCurrentDelegator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) PutCurrentDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentDelegator", reflect.TypeOf((*MockState)(nil).PutCurrentDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentDelegator", reflect.TypeOf((*MockState)(nil).PutCurrentDelegator), arg0) } // PutCurrentValidator mocks base method. -func (m *MockState) PutCurrentValidator(staker *Staker) { +func (m *MockState) PutCurrentValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutCurrentValidator", staker) + m.ctrl.Call(m, "PutCurrentValidator", arg0) } // PutCurrentValidator indicates an expected call of PutCurrentValidator. -func (mr *MockStateMockRecorder) PutCurrentValidator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) PutCurrentValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentValidator", reflect.TypeOf((*MockState)(nil).PutCurrentValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCurrentValidator", reflect.TypeOf((*MockState)(nil).PutCurrentValidator), arg0) } // PutPendingDelegator mocks base method. -func (m *MockState) PutPendingDelegator(staker *Staker) { +func (m *MockState) PutPendingDelegator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutPendingDelegator", staker) + m.ctrl.Call(m, "PutPendingDelegator", arg0) } // PutPendingDelegator indicates an expected call of PutPendingDelegator. -func (mr *MockStateMockRecorder) PutPendingDelegator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) PutPendingDelegator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingDelegator", reflect.TypeOf((*MockState)(nil).PutPendingDelegator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingDelegator", reflect.TypeOf((*MockState)(nil).PutPendingDelegator), arg0) } // PutPendingValidator mocks base method. -func (m *MockState) PutPendingValidator(staker *Staker) { +func (m *MockState) PutPendingValidator(arg0 *Staker) { m.ctrl.T.Helper() - m.ctrl.Call(m, "PutPendingValidator", staker) + m.ctrl.Call(m, "PutPendingValidator", arg0) } // PutPendingValidator indicates an expected call of PutPendingValidator. -func (mr *MockStateMockRecorder) PutPendingValidator(staker interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) PutPendingValidator(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingValidator", reflect.TypeOf((*MockState)(nil).PutPendingValidator), staker) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutPendingValidator", reflect.TypeOf((*MockState)(nil).PutPendingValidator), arg0) } // SetCurrentSupply mocks base method. -func (m *MockState) SetCurrentSupply(cs uint64) { +func (m *MockState) SetCurrentSupply(arg0 ids.ID, arg1 uint64) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetCurrentSupply", cs) + m.ctrl.Call(m, "SetCurrentSupply", arg0, arg1) } // SetCurrentSupply indicates an expected call of SetCurrentSupply. -func (mr *MockStateMockRecorder) SetCurrentSupply(cs interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) SetCurrentSupply(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentSupply", reflect.TypeOf((*MockState)(nil).SetCurrentSupply), cs) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentSupply", reflect.TypeOf((*MockState)(nil).SetCurrentSupply), arg0, arg1) } // SetHeight mocks base method. -func (m *MockState) SetHeight(height uint64) { +func (m *MockState) SetHeight(arg0 uint64) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetHeight", height) + m.ctrl.Call(m, "SetHeight", arg0) } // SetHeight indicates an expected call of SetHeight. -func (mr *MockStateMockRecorder) SetHeight(height interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) SetHeight(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeight", reflect.TypeOf((*MockState)(nil).SetHeight), height) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeight", reflect.TypeOf((*MockState)(nil).SetHeight), arg0) } // SetLastAccepted mocks base method. -func (m *MockState) SetLastAccepted(blkID ids.ID) { +func (m *MockState) SetLastAccepted(arg0 ids.ID) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetLastAccepted", blkID) + m.ctrl.Call(m, "SetLastAccepted", arg0) } // SetLastAccepted indicates an expected call of SetLastAccepted. -func (mr *MockStateMockRecorder) SetLastAccepted(blkID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) SetLastAccepted(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastAccepted", reflect.TypeOf((*MockState)(nil).SetLastAccepted), blkID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastAccepted", reflect.TypeOf((*MockState)(nil).SetLastAccepted), arg0) } // SetTimestamp mocks base method. -func (m *MockState) SetTimestamp(tm time.Time) { +func (m *MockState) SetTimestamp(arg0 time.Time) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetTimestamp", tm) + m.ctrl.Call(m, "SetTimestamp", arg0) } // SetTimestamp indicates an expected call of SetTimestamp. -func (mr *MockStateMockRecorder) SetTimestamp(tm interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) SetTimestamp(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimestamp", reflect.TypeOf((*MockState)(nil).SetTimestamp), tm) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTimestamp", reflect.TypeOf((*MockState)(nil).SetTimestamp), arg0) } // SetUptime mocks base method. -func (m *MockState) SetUptime(nodeID ids.NodeID, upDuration time.Duration, lastUpdated time.Time) error { +func (m *MockState) SetUptime(arg0 ids.NodeID, arg1 time.Duration, arg2 time.Time) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetUptime", nodeID, upDuration, lastUpdated) + ret := m.ctrl.Call(m, "SetUptime", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // SetUptime indicates an expected call of SetUptime. -func (mr *MockStateMockRecorder) SetUptime(nodeID, upDuration, lastUpdated interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) SetUptime(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUptime", reflect.TypeOf((*MockState)(nil).SetUptime), nodeID, upDuration, lastUpdated) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUptime", reflect.TypeOf((*MockState)(nil).SetUptime), arg0, arg1, arg2) } // UTXOIDs mocks base method. -func (m *MockState) UTXOIDs(addr []byte, previous ids.ID, limit int) ([]ids.ID, error) { +func (m *MockState) UTXOIDs(arg0 []byte, arg1 ids.ID, arg2 int) ([]ids.ID, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UTXOIDs", addr, previous, limit) + ret := m.ctrl.Call(m, "UTXOIDs", arg0, arg1, arg2) ret0, _ := ret[0].([]ids.ID) ret1, _ := ret[1].(error) return ret0, ret1 } // UTXOIDs indicates an expected call of UTXOIDs. -func (mr *MockStateMockRecorder) UTXOIDs(addr, previous, limit interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) UTXOIDs(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UTXOIDs", reflect.TypeOf((*MockState)(nil).UTXOIDs), addr, previous, limit) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UTXOIDs", reflect.TypeOf((*MockState)(nil).UTXOIDs), arg0, arg1, arg2) } // ValidatorSet mocks base method. -func (m *MockState) ValidatorSet(subnetID ids.ID) (validators.Set, error) { +func (m *MockState) ValidatorSet(arg0 ids.ID) (validators.Set, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidatorSet", subnetID) + ret := m.ctrl.Call(m, "ValidatorSet", arg0) ret0, _ := ret[0].(validators.Set) ret1, _ := ret[1].(error) return ret0, ret1 } // ValidatorSet indicates an expected call of ValidatorSet. -func (mr *MockStateMockRecorder) ValidatorSet(subnetID interface{}) *gomock.Call { +func (mr *MockStateMockRecorder) ValidatorSet(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorSet", reflect.TypeOf((*MockState)(nil).ValidatorSet), subnetID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorSet", reflect.TypeOf((*MockState)(nil).ValidatorSet), arg0) } diff --git a/avalanchego/vms/platformvm/state/priorities.go b/avalanchego/vms/platformvm/state/priorities.go deleted file mode 100644 index 17528126..00000000 --- a/avalanchego/vms/platformvm/state/priorities.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package state - -const ( - // First subnet delegators are removed from the current validator set, - SubnetDelegatorCurrentPriority Priority = iota + 1 - // then subnet validators, - SubnetValidatorCurrentPriority - // then primary network delegators, - PrimaryNetworkDelegatorCurrentPriority - // then primary network validators. - PrimaryNetworkValidatorCurrentPriority -) - -const ( - // First primary network delegators are moved from the pending to the - // current validator set, - PrimaryNetworkDelegatorPendingPriority Priority = iota + 1 - // then primary network validators, - PrimaryNetworkValidatorPendingPriority - // then subnet validators, - SubnetValidatorPendingPriority - // then subnet delegators. - SubnetDelegatorPendingPriority -) - -var PendingToCurrentPriorities = []Priority{ - PrimaryNetworkValidatorPendingPriority: PrimaryNetworkValidatorCurrentPriority, - PrimaryNetworkDelegatorPendingPriority: PrimaryNetworkDelegatorCurrentPriority, - SubnetValidatorPendingPriority: SubnetValidatorCurrentPriority, - SubnetDelegatorPendingPriority: SubnetDelegatorCurrentPriority, -} - -type Priority byte diff --git a/avalanchego/vms/platformvm/state/slice_iterator_test.go b/avalanchego/vms/platformvm/state/slice_iterator_test.go index 53b5e8b3..8dbf4ed3 100644 --- a/avalanchego/vms/platformvm/state/slice_iterator_test.go +++ b/avalanchego/vms/platformvm/state/slice_iterator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/staker.go b/avalanchego/vms/platformvm/state/staker.go index 36cfda7a..afc19bb0 100644 --- a/avalanchego/vms/platformvm/state/staker.go +++ b/avalanchego/vms/platformvm/state/staker.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -10,8 +10,7 @@ import ( "github.com/google/btree" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) var _ btree.Item = &Staker{} @@ -53,7 +52,7 @@ type Staker struct { // are grouped together. The ordering of these groups is documented in // [priorities.go] and depends on if the stakers are in the pending or // current validator set. - Priority Priority + Priority txs.Priority } // A *Staker is considered to be less than another *Staker when: @@ -84,24 +83,31 @@ func (s *Staker) Less(thanIntf btree.Item) bool { return bytes.Compare(s.TxID[:], than.TxID[:]) == -1 } -func NewPrimaryNetworkStaker(txID ids.ID, vdr *validator.Validator) *Staker { +func NewCurrentStaker(txID ids.ID, staker txs.Staker, potentialReward uint64) *Staker { + endTime := staker.EndTime() return &Staker{ - TxID: txID, - NodeID: vdr.ID(), - SubnetID: constants.PrimaryNetworkID, - Weight: vdr.Weight(), - StartTime: vdr.StartTime(), - EndTime: vdr.EndTime(), + TxID: txID, + NodeID: staker.NodeID(), + SubnetID: staker.SubnetID(), + Weight: staker.Weight(), + StartTime: staker.StartTime(), + EndTime: endTime, + PotentialReward: potentialReward, + NextTime: endTime, + Priority: staker.CurrentPriority(), } } -func NewSubnetStaker(txID ids.ID, vdr *validator.SubnetValidator) *Staker { +func NewPendingStaker(txID ids.ID, staker txs.Staker) *Staker { + startTime := staker.StartTime() return &Staker{ TxID: txID, - NodeID: vdr.ID(), - SubnetID: vdr.SubnetID(), - Weight: vdr.Weight(), - StartTime: vdr.StartTime(), - EndTime: vdr.EndTime(), + NodeID: staker.NodeID(), + SubnetID: staker.SubnetID(), + Weight: staker.Weight(), + StartTime: startTime, + EndTime: staker.EndTime(), + NextTime: startTime, + Priority: staker.PendingPriority(), } } diff --git a/avalanchego/vms/platformvm/state/staker_diff_iterator.go b/avalanchego/vms/platformvm/state/staker_diff_iterator.go index c8565001..d3691ef9 100644 --- a/avalanchego/vms/platformvm/state/staker_diff_iterator.go +++ b/avalanchego/vms/platformvm/state/staker_diff_iterator.go @@ -1,10 +1,12 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state import ( "container/heap" + + "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) var ( @@ -104,7 +106,7 @@ func (it *stakerDiffIterator) advancePending() { toRemove := *it.modifiedStaker toRemove.NextTime = toRemove.EndTime - toRemove.Priority = PendingToCurrentPriorities[toRemove.Priority] + toRemove.Priority = txs.PendingToCurrentPriorities[toRemove.Priority] it.currentIteratorExhausted = false it.currentIterator.Add(&toRemove) } diff --git a/avalanchego/vms/platformvm/state/staker_diff_iterator_test.go b/avalanchego/vms/platformvm/state/staker_diff_iterator_test.go index 3e2242d8..4f23acfc 100644 --- a/avalanchego/vms/platformvm/state/staker_diff_iterator_test.go +++ b/avalanchego/vms/platformvm/state/staker_diff_iterator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) func TestStakerDiffIterator(t *testing.T) { @@ -19,7 +20,7 @@ func TestStakerDiffIterator(t *testing.T) { TxID: ids.GenerateTestID(), EndTime: time.Unix(10, 0), NextTime: time.Unix(10, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, } pendingStakers := []*Staker{ @@ -28,28 +29,28 @@ func TestStakerDiffIterator(t *testing.T) { StartTime: time.Unix(0, 0), EndTime: time.Unix(5, 0), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkDelegatorPendingPriority, + Priority: txs.PrimaryNetworkDelegatorApricotPendingPriority, }, { TxID: ids.GenerateTestID(), StartTime: time.Unix(5, 0), EndTime: time.Unix(10, 0), NextTime: time.Unix(5, 0), - Priority: PrimaryNetworkDelegatorPendingPriority, + Priority: txs.PrimaryNetworkDelegatorApricotPendingPriority, }, { TxID: ids.GenerateTestID(), StartTime: time.Unix(11, 0), EndTime: time.Unix(20, 0), NextTime: time.Unix(11, 0), - Priority: PrimaryNetworkValidatorPendingPriority, + Priority: txs.PrimaryNetworkValidatorPendingPriority, }, { TxID: ids.GenerateTestID(), StartTime: time.Unix(11, 0), EndTime: time.Unix(20, 0), NextTime: time.Unix(11, 0), - Priority: PrimaryNetworkDelegatorPendingPriority, + Priority: txs.PrimaryNetworkDelegatorApricotPendingPriority, }, } @@ -117,19 +118,19 @@ func TestMutableStakerIterator(t *testing.T) { TxID: ids.GenerateTestID(), EndTime: time.Unix(10, 0), NextTime: time.Unix(10, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, { TxID: ids.GenerateTestID(), EndTime: time.Unix(20, 0), NextTime: time.Unix(20, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, { TxID: ids.GenerateTestID(), EndTime: time.Unix(30, 0), NextTime: time.Unix(30, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, } @@ -140,19 +141,19 @@ func TestMutableStakerIterator(t *testing.T) { TxID: ids.GenerateTestID(), EndTime: time.Unix(5, 0), NextTime: time.Unix(5, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, { TxID: ids.GenerateTestID(), EndTime: time.Unix(15, 0), NextTime: time.Unix(15, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, { TxID: ids.GenerateTestID(), EndTime: time.Unix(25, 0), NextTime: time.Unix(25, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, } diff --git a/avalanchego/vms/platformvm/state/staker_test.go b/avalanchego/vms/platformvm/state/staker_test.go index a2833bb2..b4678525 100644 --- a/avalanchego/vms/platformvm/state/staker_test.go +++ b/avalanchego/vms/platformvm/state/staker_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -7,11 +7,12 @@ import ( "testing" "time" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) func TestStakerLess(t *testing.T) { @@ -26,12 +27,12 @@ func TestStakerLess(t *testing.T) { left: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, right: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(1, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, less: true, }, @@ -40,12 +41,12 @@ func TestStakerLess(t *testing.T) { left: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(1, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, right: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, less: false, }, @@ -54,12 +55,12 @@ func TestStakerLess(t *testing.T) { left: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkDelegatorPendingPriority, + Priority: txs.PrimaryNetworkDelegatorApricotPendingPriority, }, right: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorPendingPriority, + Priority: txs.PrimaryNetworkValidatorPendingPriority, }, less: true, }, @@ -68,12 +69,12 @@ func TestStakerLess(t *testing.T) { left: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorPendingPriority, + Priority: txs.PrimaryNetworkValidatorPendingPriority, }, right: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkDelegatorPendingPriority, + Priority: txs.PrimaryNetworkDelegatorApricotPendingPriority, }, less: false, }, @@ -82,12 +83,12 @@ func TestStakerLess(t *testing.T) { left: &Staker{ TxID: ids.ID([32]byte{0}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorPendingPriority, + Priority: txs.PrimaryNetworkValidatorPendingPriority, }, right: &Staker{ TxID: ids.ID([32]byte{1}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorPendingPriority, + Priority: txs.PrimaryNetworkValidatorPendingPriority, }, less: true, }, @@ -96,12 +97,12 @@ func TestStakerLess(t *testing.T) { left: &Staker{ TxID: ids.ID([32]byte{1}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorPendingPriority, + Priority: txs.PrimaryNetworkValidatorPendingPriority, }, right: &Staker{ TxID: ids.ID([32]byte{0}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorPendingPriority, + Priority: txs.PrimaryNetworkValidatorPendingPriority, }, less: false, }, @@ -110,12 +111,12 @@ func TestStakerLess(t *testing.T) { left: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, right: &Staker{ TxID: ids.ID([32]byte{}), NextTime: time.Unix(0, 0), - Priority: PrimaryNetworkValidatorCurrentPriority, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, }, less: false, }, @@ -127,51 +128,71 @@ func TestStakerLess(t *testing.T) { } } -func TestNewPrimaryNetworkStaker(t *testing.T) { +func TestNewCurrentStaker(t *testing.T) { require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + txID := ids.GenerateTestID() - vdr := &validator.Validator{ - NodeID: ids.GenerateTestNodeID(), - Start: 0, - End: 1, - Wght: 2, - } + nodeID := ids.GenerateTestNodeID() + subnetID := ids.GenerateTestID() + weight := uint64(12345) + startTime := time.Now() + endTime := time.Now() + potentialReward := uint64(54321) + currentPriority := txs.SubnetPermissionedValidatorCurrentPriority - staker := NewPrimaryNetworkStaker(txID, vdr) + stakerTx := txs.NewMockStaker(ctrl) + stakerTx.EXPECT().NodeID().Return(nodeID) + stakerTx.EXPECT().SubnetID().Return(subnetID) + stakerTx.EXPECT().Weight().Return(weight) + stakerTx.EXPECT().StartTime().Return(startTime) + stakerTx.EXPECT().EndTime().Return(endTime) + stakerTx.EXPECT().CurrentPriority().Return(currentPriority) + + staker := NewCurrentStaker(txID, stakerTx, potentialReward) require.NotNil(staker) require.Equal(txID, staker.TxID) - require.Equal(vdr.NodeID, staker.NodeID) - require.Equal(constants.PrimaryNetworkID, staker.SubnetID) - require.Equal(vdr.Wght, staker.Weight) - require.Equal(vdr.StartTime(), staker.StartTime) - require.Equal(vdr.EndTime(), staker.EndTime) - require.Zero(staker.PotentialReward) - require.Zero(staker.NextTime) - require.Zero(staker.Priority) + require.Equal(nodeID, staker.NodeID) + require.Equal(subnetID, staker.SubnetID) + require.Equal(weight, staker.Weight) + require.Equal(startTime, staker.StartTime) + require.Equal(endTime, staker.EndTime) + require.Equal(potentialReward, staker.PotentialReward) + require.Equal(endTime, staker.NextTime) + require.Equal(currentPriority, staker.Priority) } -func TestNewSubnetStaker(t *testing.T) { +func TestNewPendingStaker(t *testing.T) { require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + txID := ids.GenerateTestID() - vdr := &validator.SubnetValidator{ - Validator: validator.Validator{ - NodeID: ids.GenerateTestNodeID(), - Start: 0, - End: 1, - Wght: 2, - }, - Subnet: ids.GenerateTestID(), - } + nodeID := ids.GenerateTestNodeID() + subnetID := ids.GenerateTestID() + weight := uint64(12345) + startTime := time.Now() + endTime := time.Now() + pendingPriority := txs.SubnetPermissionedValidatorPendingPriority + + stakerTx := txs.NewMockStaker(ctrl) + stakerTx.EXPECT().NodeID().Return(nodeID) + stakerTx.EXPECT().SubnetID().Return(subnetID) + stakerTx.EXPECT().Weight().Return(weight) + stakerTx.EXPECT().StartTime().Return(startTime) + stakerTx.EXPECT().EndTime().Return(endTime) + stakerTx.EXPECT().PendingPriority().Return(pendingPriority) - staker := NewSubnetStaker(txID, vdr) + staker := NewPendingStaker(txID, stakerTx) require.NotNil(staker) require.Equal(txID, staker.TxID) - require.Equal(vdr.NodeID, staker.NodeID) - require.Equal(vdr.Subnet, staker.SubnetID) - require.Equal(vdr.Wght, staker.Weight) - require.Equal(vdr.StartTime(), staker.StartTime) - require.Equal(vdr.EndTime(), staker.EndTime) + require.Equal(nodeID, staker.NodeID) + require.Equal(subnetID, staker.SubnetID) + require.Equal(weight, staker.Weight) + require.Equal(startTime, staker.StartTime) + require.Equal(endTime, staker.EndTime) require.Zero(staker.PotentialReward) - require.Zero(staker.NextTime) - require.Zero(staker.Priority) + require.Equal(startTime, staker.NextTime) + require.Equal(pendingPriority, staker.Priority) } diff --git a/avalanchego/vms/platformvm/state/stakers.go b/avalanchego/vms/platformvm/state/stakers.go index dedef6a6..b3deae01 100644 --- a/avalanchego/vms/platformvm/state/stakers.go +++ b/avalanchego/vms/platformvm/state/stakers.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -23,10 +23,14 @@ type CurrentStakers interface { // PutCurrentValidator adds the [staker] describing a validator to the // staker set. + // + // Invariant: [staker] is not currently a CurrentValidator PutCurrentValidator(staker *Staker) // DeleteCurrentValidator removes the [staker] describing a validator from // the staker set. + // + // Invariant: [staker] is currently a CurrentValidator DeleteCurrentValidator(staker *Staker) // GetCurrentDelegatorIterator returns the delegators associated with the @@ -36,10 +40,14 @@ type CurrentStakers interface { // PutCurrentDelegator adds the [staker] describing a delegator to the // staker set. + // + // Invariant: [staker] is not currently a CurrentDelegator PutCurrentDelegator(staker *Staker) // DeleteCurrentDelegator removes the [staker] describing a delegator from // the staker set. + // + // Invariant: [staker] is currently a CurrentDelegator DeleteCurrentDelegator(staker *Staker) // GetCurrentStakerIterator returns stakers in order of their removal from diff --git a/avalanchego/vms/platformvm/state/stakers_test.go b/avalanchego/vms/platformvm/state/stakers_test.go index db96bb73..d022d9cd 100644 --- a/avalanchego/vms/platformvm/state/stakers_test.go +++ b/avalanchego/vms/platformvm/state/stakers_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) func TestBaseStakersPruning(t *testing.T) { @@ -208,7 +209,7 @@ func newTestStaker() *Staker { PotentialReward: 1, NextTime: endTime, - Priority: PrimaryNetworkDelegatorCurrentPriority, + Priority: txs.PrimaryNetworkDelegatorCurrentPriority, } } diff --git a/avalanchego/vms/platformvm/state/state.go b/avalanchego/vms/platformvm/state/state.go index 881cd8b4..4d7ca7b0 100644 --- a/avalanchego/vms/platformvm/state/state.go +++ b/avalanchego/vms/platformvm/state/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -51,20 +51,23 @@ var ( ErrDelegatorSubset = errors.New("delegator's time range must be a subset of the validator's time range") - blockPrefix = []byte("block") - validatorsPrefix = []byte("validators") - currentPrefix = []byte("current") - pendingPrefix = []byte("pending") - validatorPrefix = []byte("validator") - delegatorPrefix = []byte("delegator") - subnetValidatorPrefix = []byte("subnetValidator") - validatorDiffsPrefix = []byte("validatorDiffs") - txPrefix = []byte("tx") - rewardUTXOsPrefix = []byte("rewardUTXOs") - utxoPrefix = []byte("utxo") - subnetPrefix = []byte("subnet") - chainPrefix = []byte("chain") - singletonPrefix = []byte("singleton") + blockPrefix = []byte("block") + validatorsPrefix = []byte("validators") + currentPrefix = []byte("current") + pendingPrefix = []byte("pending") + validatorPrefix = []byte("validator") + delegatorPrefix = []byte("delegator") + subnetValidatorPrefix = []byte("subnetValidator") + subnetDelegatorPrefix = []byte("subnetDelegator") + validatorDiffsPrefix = []byte("validatorDiffs") + txPrefix = []byte("tx") + rewardUTXOsPrefix = []byte("rewardUTXOs") + utxoPrefix = []byte("utxo") + subnetPrefix = []byte("subnet") + transformedSubnetPrefix = []byte("transformedSubnet") + supplyPrefix = []byte("supply") + chainPrefix = []byte("chain") + singletonPrefix = []byte("singleton") timestampKey = []byte("timestamp") currentSupplyKey = []byte("current supply") @@ -85,15 +88,22 @@ type Chain interface { GetTimestamp() time.Time SetTimestamp(tm time.Time) - GetCurrentSupply() uint64 - SetCurrentSupply(cs uint64) + + GetCurrentSupply(subnetID ids.ID) (uint64, error) + SetCurrentSupply(subnetID ids.ID, cs uint64) GetRewardUTXOs(txID ids.ID) ([]*avax.UTXO, error) AddRewardUTXO(txID ids.ID, utxo *avax.UTXO) + GetSubnets() ([]*txs.Tx, error) AddSubnet(createSubnetTx *txs.Tx) + + GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) + AddSubnetTransformation(transformSubnetTx *txs.Tx) + GetChains(subnetID ids.ID) ([]*txs.Tx, error) AddChain(createChainTx *txs.Tx) + GetTx(txID ids.ID) (*txs.Tx, status.Status, error) AddTx(tx *txs.Tx, status status.Status) } @@ -151,9 +161,12 @@ type stateBlk struct { * | | |-. delegator * | | | '-. list * | | | '-- txID -> potential reward - * | | '-. subnetValidator + * | | |-. subnetValidator + * | | | '-. list + * | | | '-- txID -> potential reward or nil + * | | '-. subnetDelegator * | | '-. list - * | | '-- txID -> nil + * | | '-- txID -> potential reward * | |-. pending * | | |-. validator * | | | '-. list @@ -161,7 +174,10 @@ type stateBlk struct { * | | |-. delegator * | | | '-. list * | | | '-- txID -> nil - * | | '-. subnetValidator + * | | |-. subnetValidator + * | | | '-. list + * | | | '-- txID -> nil + * | | '-. subnetDelegator * | | '-. list * | | '-- txID -> nil * | '-. diffs @@ -219,6 +235,8 @@ type state struct { currentDelegatorList linkeddb.LinkedDB currentSubnetValidatorBaseDB database.Database currentSubnetValidatorList linkeddb.LinkedDB + currentSubnetDelegatorBaseDB database.Database + currentSubnetDelegatorList linkeddb.LinkedDB pendingValidatorsDB database.Database pendingValidatorBaseDB database.Database pendingValidatorList linkeddb.LinkedDB @@ -226,6 +244,8 @@ type state struct { pendingDelegatorList linkeddb.LinkedDB pendingSubnetValidatorBaseDB database.Database pendingSubnetValidatorList linkeddb.LinkedDB + pendingSubnetDelegatorBaseDB database.Database + pendingSubnetDelegatorList linkeddb.LinkedDB validatorDiffsCache cache.Cacher // cache of heightWithSubnet -> map[ids.ShortID]*ValidatorWeightDiff validatorDiffsDB database.Database @@ -247,6 +267,14 @@ type state struct { subnetBaseDB database.Database subnetDB linkeddb.LinkedDB + transformedSubnets map[ids.ID]*txs.Tx // map of subnetID -> transformSubnetTx + transformedSubnetCache cache.Cacher // cache of subnetID -> transformSubnetTx if the entry is nil, it is not in the database + transformedSubnetDB database.Database + + modifiedSupplies map[ids.ID]uint64 // map of subnetID -> current supply + supplyCache cache.Cacher // cache of subnetID -> current supply if the entry is nil, it is not in the database + supplyDB database.Database + addedChains map[ids.ID][]*txs.Tx // maps subnetID -> the newly added chains to the subnet chainCache cache.Cacher // cache of subnetID -> the chains after all local modifications []*txs.Tx chainDBCache cache.Cacher // cache of subnetID -> linkedDB @@ -333,7 +361,6 @@ func New( return nil, err } - // SGB-MERGE set uptimes of default validators for _, vdr := range validators.DefaultValidatorList() { s.uptimes[vdr.ID()] = &uptimeAndReward{ txID: ids.Empty, @@ -369,11 +396,13 @@ func new( currentValidatorBaseDB := prefixdb.New(validatorPrefix, currentValidatorsDB) currentDelegatorBaseDB := prefixdb.New(delegatorPrefix, currentValidatorsDB) currentSubnetValidatorBaseDB := prefixdb.New(subnetValidatorPrefix, currentValidatorsDB) + currentSubnetDelegatorBaseDB := prefixdb.New(subnetDelegatorPrefix, currentValidatorsDB) pendingValidatorsDB := prefixdb.New(pendingPrefix, validatorsDB) pendingValidatorBaseDB := prefixdb.New(validatorPrefix, pendingValidatorsDB) pendingDelegatorBaseDB := prefixdb.New(delegatorPrefix, pendingValidatorsDB) pendingSubnetValidatorBaseDB := prefixdb.New(subnetValidatorPrefix, pendingValidatorsDB) + pendingSubnetDelegatorBaseDB := prefixdb.New(subnetDelegatorPrefix, pendingValidatorsDB) validatorDiffsDB := prefixdb.New(validatorDiffsPrefix, validatorsDB) @@ -406,13 +435,31 @@ func new( } utxoDB := prefixdb.New(utxoPrefix, baseDB) - utxoState, err := avax.NewMeteredUTXOState(utxoDB, genesis.Codec, metricsReg) + utxoState, err := avax.NewMeteredUTXOState(utxoDB, txs.GenesisCodec, metricsReg) if err != nil { return nil, err } subnetBaseDB := prefixdb.New(subnetPrefix, baseDB) + transformedSubnetCache, err := metercacher.New( + "transformed_subnet_cache", + metricsReg, + &cache.LRU{Size: chainCacheSize}, + ) + if err != nil { + return nil, err + } + + supplyCache, err := metercacher.New( + "supply_cache", + metricsReg, + &cache.LRU{Size: chainCacheSize}, + ) + if err != nil { + return nil, err + } + chainCache, err := metercacher.New( "chain_cache", metricsReg, @@ -456,6 +503,8 @@ func new( currentDelegatorList: linkeddb.NewDefault(currentDelegatorBaseDB), currentSubnetValidatorBaseDB: currentSubnetValidatorBaseDB, currentSubnetValidatorList: linkeddb.NewDefault(currentSubnetValidatorBaseDB), + currentSubnetDelegatorBaseDB: currentSubnetDelegatorBaseDB, + currentSubnetDelegatorList: linkeddb.NewDefault(currentSubnetDelegatorBaseDB), pendingValidatorsDB: pendingValidatorsDB, pendingValidatorBaseDB: pendingValidatorBaseDB, pendingValidatorList: linkeddb.NewDefault(pendingValidatorBaseDB), @@ -463,6 +512,8 @@ func new( pendingDelegatorList: linkeddb.NewDefault(pendingDelegatorBaseDB), pendingSubnetValidatorBaseDB: pendingSubnetValidatorBaseDB, pendingSubnetValidatorList: linkeddb.NewDefault(pendingSubnetValidatorBaseDB), + pendingSubnetDelegatorBaseDB: pendingSubnetDelegatorBaseDB, + pendingSubnetDelegatorList: linkeddb.NewDefault(pendingSubnetDelegatorBaseDB), validatorDiffsDB: validatorDiffsDB, validatorDiffsCache: validatorDiffsCache, @@ -481,6 +532,14 @@ func new( subnetBaseDB: subnetBaseDB, subnetDB: linkeddb.NewDefault(subnetBaseDB), + transformedSubnets: make(map[ids.ID]*txs.Tx), + transformedSubnetCache: transformedSubnetCache, + transformedSubnetDB: prefixdb.New(transformedSubnetPrefix, baseDB), + + modifiedSupplies: make(map[ids.ID]uint64), + supplyCache: supplyCache, + supplyDB: prefixdb.New(supplyPrefix, baseDB), + addedChains: make(map[ids.ID][]*txs.Tx), chainDB: prefixdb.New(chainPrefix, baseDB), chainCache: chainCache, @@ -595,6 +654,40 @@ func (s *state) AddSubnet(createSubnetTx *txs.Tx) { } } +func (s *state) GetSubnetTransformation(subnetID ids.ID) (*txs.Tx, error) { + if tx, exists := s.transformedSubnets[subnetID]; exists { + return tx, nil + } + + if txIntf, cached := s.transformedSubnetCache.Get(subnetID); cached { + if txIntf == nil { + return nil, database.ErrNotFound + } + return txIntf.(*txs.Tx), nil + } + + transformSubnetTxID, err := database.GetID(s.transformedSubnetDB, subnetID[:]) + if err == database.ErrNotFound { + s.transformedSubnetCache.Put(subnetID, nil) + return nil, database.ErrNotFound + } + if err != nil { + return nil, err + } + + transformSubnetTx, _, err := s.GetTx(transformSubnetTxID) + if err != nil { + return nil, err + } + s.transformedSubnetCache.Put(subnetID, transformSubnetTx) + return transformSubnetTx, nil +} + +func (s *state) AddSubnetTransformation(transformSubnetTxIntf *txs.Tx) { + transformSubnetTx := transformSubnetTxIntf.Unsigned.(*txs.TransformSubnetTx) + s.transformedSubnets[transformSubnetTx.Subnet] = transformSubnetTxIntf +} + func (s *state) GetChains(subnetID ids.ID) ([]*txs.Tx, error) { if chainsIntf, cached := s.chainCache.Get(subnetID); cached { return chainsIntf.([]*txs.Tx), nil @@ -665,11 +758,11 @@ func (s *state) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) { } stx := txBytesAndStatus{} - if _, err := genesis.Codec.Unmarshal(txBytes, &stx); err != nil { + if _, err := txs.GenesisCodec.Unmarshal(txBytes, &stx); err != nil { return nil, status.Unknown, err } - tx, err := txs.Parse(genesis.Codec, stx.Tx) + tx, err := txs.Parse(txs.GenesisCodec, stx.Tx) if err != nil { return nil, status.Unknown, err } @@ -776,17 +869,54 @@ func (s *state) GetStartTime(nodeID ids.NodeID) (time.Time, error) { func (s *state) GetTimestamp() time.Time { return s.timestamp } func (s *state) SetTimestamp(tm time.Time) { s.timestamp = tm } -func (s *state) GetCurrentSupply() uint64 { return s.currentSupply } -func (s *state) SetCurrentSupply(cs uint64) { s.currentSupply = cs } func (s *state) GetLastAccepted() ids.ID { return s.lastAccepted } func (s *state) SetLastAccepted(lastAccepted ids.ID) { s.lastAccepted = lastAccepted } +func (s *state) GetCurrentSupply(subnetID ids.ID) (uint64, error) { + if subnetID == constants.PrimaryNetworkID { + return s.currentSupply, nil + } + + supply, ok := s.modifiedSupplies[subnetID] + if ok { + return supply, nil + } + + supplyIntf, ok := s.supplyCache.Get(subnetID) + if ok { + if supplyIntf == nil { + return 0, database.ErrNotFound + } + return supplyIntf.(uint64), nil + } + + supply, err := database.GetUInt64(s.supplyDB, subnetID[:]) + if err == database.ErrNotFound { + s.supplyCache.Put(subnetID, nil) + return 0, database.ErrNotFound + } + if err != nil { + return 0, err + } + + s.supplyCache.Put(subnetID, supply) + return supply, nil +} + +func (s *state) SetCurrentSupply(subnetID ids.ID, cs uint64) { + if subnetID == constants.PrimaryNetworkID { + s.currentSupply = cs + } else { + s.modifiedSupplies[subnetID] = cs + } +} + func (s *state) GetValidatorWeightDiffs(height uint64, subnetID ids.ID) (map[ids.NodeID]*ValidatorWeightDiff, error) { prefixStruct := heightWithSubnet{ Height: height, SubnetID: subnetID, } - prefixBytes, err := genesis.Codec.Marshal(txs.Version, prefixStruct) + prefixBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, prefixStruct) if err != nil { return nil, err } @@ -809,7 +939,7 @@ func (s *state) GetValidatorWeightDiffs(height uint64, subnetID ids.ID) (map[ids } weightDiff := ValidatorWeightDiff{} - _, err = genesis.Codec.Unmarshal(diffIter.Value(), &weightDiff) + _, err = blocks.GenesisCodec.Unmarshal(diffIter.Value(), &weightDiff) if err != nil { return nil, err } @@ -844,11 +974,11 @@ func (s *state) ValidatorSet(subnetID ids.ID) (validators.Set, error) { return vdrs, nil } -func (s *state) syncGenesis(genesisBlk *blocks.CommitBlock, genesis *genesis.State) error { +func (s *state) syncGenesis(genesisBlk blocks.Block, genesis *genesis.State) error { genesisBlkID := genesisBlk.ID() s.SetLastAccepted(genesisBlkID) s.SetTimestamp(time.Unix(int64(genesis.Timestamp), 0)) - s.SetCurrentSupply(genesis.InitialSupply) + s.SetCurrentSupply(constants.PrimaryNetworkID, genesis.InitialSupply) s.AddStatelessBlock(genesisBlk, choices.Accepted) // Persist UTXOs that exist at genesis @@ -865,7 +995,10 @@ func (s *state) syncGenesis(genesisBlk *blocks.CommitBlock, genesis *genesis.Sta stakeAmount := tx.Validator.Wght stakeDuration := tx.Validator.Duration() - currentSupply := s.GetCurrentSupply() + currentSupply, err := s.GetCurrentSupply(constants.PrimaryNetworkID) + if err != nil { + return err + } potentialReward := s.rewards.Calculate( stakeDuration, @@ -877,14 +1010,10 @@ func (s *state) syncGenesis(genesisBlk *blocks.CommitBlock, genesis *genesis.Sta return err } - staker := NewPrimaryNetworkStaker(vdrTx.ID(), &tx.Validator) - staker.PotentialReward = potentialReward - staker.NextTime = staker.EndTime - staker.Priority = PrimaryNetworkValidatorCurrentPriority - + staker := NewCurrentStaker(vdrTx.ID(), tx, potentialReward) s.PutCurrentValidator(staker) s.AddTx(vdrTx, status.Committed) - s.SetCurrentSupply(newCurrentSupply) + s.SetCurrentSupply(constants.PrimaryNetworkID, newCurrentSupply) } for _, chain := range genesis.Chains { @@ -902,6 +1031,9 @@ func (s *state) syncGenesis(genesisBlk *blocks.CommitBlock, genesis *genesis.Sta s.AddChain(chain) s.AddTx(chain, status.Committed) } + + validators.InitializeDefaultValidators(s.ctx.NetworkID, s.GetTimestamp()) + return s.write(0) } @@ -913,6 +1045,9 @@ func (s *state) load() error { s.loadCurrentValidators(), s.loadPendingValidators(), ) + + validators.InitializeDefaultValidators(s.ctx.NetworkID, s.GetTimestamp()) + return errs.Err } @@ -929,7 +1064,7 @@ func (s *state) loadMetadata() error { return err } s.persistedCurrentSupply = currentSupply - s.SetCurrentSupply(currentSupply) + s.SetCurrentSupply(constants.PrimaryNetworkID, currentSupply) lastAccepted, err := database.GetID(s.singletonDB, lastAcceptedKey) if err != nil { @@ -965,32 +1100,24 @@ func (s *state) loadCurrentValidators() error { } uptime.lastUpdated = time.Unix(int64(uptime.LastUpdated), 0) - addValidatorTx, ok := tx.Unsigned.(*txs.AddValidatorTx) + stakerTx, ok := tx.Unsigned.(txs.Staker) if !ok { - return fmt.Errorf("expected tx type *txs.AddValidatorTx but got %T", tx.Unsigned) + return fmt.Errorf("expected tx type txs.Staker but got %T", tx.Unsigned) } - staker := NewPrimaryNetworkStaker(txID, &addValidatorTx.Validator) - staker.PotentialReward = uptime.PotentialReward - staker.NextTime = staker.EndTime - staker.Priority = PrimaryNetworkValidatorCurrentPriority - + staker := NewCurrentStaker(txID, stakerTx, uptime.PotentialReward) validator := s.currentStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) validator.validator = staker s.currentStakers.stakers.ReplaceOrInsert(staker) - s.uptimes[addValidatorTx.Validator.NodeID] = uptime + s.uptimes[staker.NodeID] = uptime } - if err := validatorIt.Error(); err != nil { - return err - } - - delegatorIt := s.currentDelegatorList.NewIterator() - defer delegatorIt.Release() - for delegatorIt.Next() { - txIDBytes := delegatorIt.Key() + subnetValidatorIt := s.currentSubnetValidatorList.NewIterator() + defer subnetValidatorIt.Release() + for subnetValidatorIt.Next() { + txIDBytes := subnetValidatorIt.Key() txID, err := ids.ToID(txIDBytes) if err != nil { return err @@ -1000,62 +1127,77 @@ func (s *state) loadCurrentValidators() error { return err } - potentialRewardBytes := delegatorIt.Value() - potentialReward, err := database.ParseUInt64(potentialRewardBytes) - if err != nil { - return err + // Because permissioned validators originally wrote their values as nil, + // we handle empty [potentialRewardBytes] as 0. + var potentialReward uint64 + potentialRewardBytes := subnetValidatorIt.Value() + if len(potentialRewardBytes) > 0 { + potentialReward, err = database.ParseUInt64(potentialRewardBytes) + if err != nil { + return err + } } - addDelegatorTx, ok := tx.Unsigned.(*txs.AddDelegatorTx) + stakerTx, ok := tx.Unsigned.(txs.Staker) if !ok { - return fmt.Errorf("expected tx type *txs.AddDelegatorTx but got %T", tx.Unsigned) + return fmt.Errorf("expected tx type txs.Staker but got %T", tx.Unsigned) } - staker := NewPrimaryNetworkStaker(txID, &addDelegatorTx.Validator) - staker.PotentialReward = potentialReward - staker.NextTime = staker.EndTime - staker.Priority = PrimaryNetworkDelegatorCurrentPriority - + staker := NewCurrentStaker(txID, stakerTx, potentialReward) validator := s.currentStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) - if validator.delegators == nil { - validator.delegators = btree.New(defaultTreeDegree) - } - validator.delegators.ReplaceOrInsert(staker) + validator.validator = staker s.currentStakers.stakers.ReplaceOrInsert(staker) } - if err := delegatorIt.Error(); err != nil { - return err - } - subnetValidatorIt := s.currentSubnetValidatorList.NewIterator() - defer subnetValidatorIt.Release() - for subnetValidatorIt.Next() { - txIDBytes := subnetValidatorIt.Key() - txID, err := ids.ToID(txIDBytes) - if err != nil { - return err - } - tx, _, err := s.GetTx(txID) - if err != nil { - return err - } + delegatorIt := s.currentDelegatorList.NewIterator() + defer delegatorIt.Release() - addSubnetValidatorTx, ok := tx.Unsigned.(*txs.AddSubnetValidatorTx) - if !ok { - return fmt.Errorf("expected tx type *txs.AddSubnetValidatorTx but got %T", tx.Unsigned) - } + subnetDelegatorIt := s.currentSubnetDelegatorList.NewIterator() + defer subnetDelegatorIt.Release() - staker := NewSubnetStaker(txID, &addSubnetValidatorTx.Validator) - staker.NextTime = staker.EndTime - staker.Priority = SubnetValidatorCurrentPriority + for _, delegatorIt := range []database.Iterator{delegatorIt, subnetDelegatorIt} { + for delegatorIt.Next() { + txIDBytes := delegatorIt.Key() + txID, err := ids.ToID(txIDBytes) + if err != nil { + return err + } + tx, _, err := s.GetTx(txID) + if err != nil { + return err + } - validator := s.currentStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) - validator.validator = staker + potentialRewardBytes := delegatorIt.Value() + potentialReward, err := database.ParseUInt64(potentialRewardBytes) + if err != nil { + return err + } - s.currentStakers.stakers.ReplaceOrInsert(staker) + stakerTx, ok := tx.Unsigned.(txs.Staker) + if !ok { + return fmt.Errorf("expected tx type txs.Staker but got %T", tx.Unsigned) + } + + staker := NewCurrentStaker(txID, stakerTx, potentialReward) + validator := s.currentStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) + if validator.delegators == nil { + validator.delegators = btree.New(defaultTreeDegree) + } + validator.delegators.ReplaceOrInsert(staker) + + s.currentStakers.stakers.ReplaceOrInsert(staker) + } } - return subnetValidatorIt.Error() + + errs := wrappers.Errs{} + errs.Add( + validatorIt.Error(), + subnetValidatorIt.Error(), + delegatorIt.Error(), + subnetDelegatorIt.Error(), + ) + return errs.Err } func (s *state) loadPendingValidators() error { @@ -1063,97 +1205,77 @@ func (s *state) loadPendingValidators() error { validatorIt := s.pendingValidatorList.NewIterator() defer validatorIt.Release() - for validatorIt.Next() { - txIDBytes := validatorIt.Key() - txID, err := ids.ToID(txIDBytes) - if err != nil { - return err - } - tx, _, err := s.GetTx(txID) - if err != nil { - return err - } - addValidatorTx, ok := tx.Unsigned.(*txs.AddValidatorTx) - if !ok { - return fmt.Errorf("expected tx type *txs.AddValidatorTx but got %T", tx.Unsigned) - } + subnetValidatorIt := s.pendingSubnetValidatorList.NewIterator() + defer subnetValidatorIt.Release() + + for _, validatorIt := range []database.Iterator{validatorIt, subnetValidatorIt} { + for validatorIt.Next() { + txIDBytes := validatorIt.Key() + txID, err := ids.ToID(txIDBytes) + if err != nil { + return err + } + tx, _, err := s.GetTx(txID) + if err != nil { + return err + } - staker := NewPrimaryNetworkStaker(txID, &addValidatorTx.Validator) - staker.NextTime = staker.StartTime - staker.Priority = PrimaryNetworkValidatorPendingPriority + stakerTx, ok := tx.Unsigned.(txs.Staker) + if !ok { + return fmt.Errorf("expected tx type txs.Staker but got %T", tx.Unsigned) + } - validator := s.pendingStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) - validator.validator = staker + staker := NewPendingStaker(txID, stakerTx) + validator := s.pendingStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) + validator.validator = staker - s.pendingStakers.stakers.ReplaceOrInsert(staker) - } - if err := validatorIt.Error(); err != nil { - return err + s.pendingStakers.stakers.ReplaceOrInsert(staker) + } } delegatorIt := s.pendingDelegatorList.NewIterator() defer delegatorIt.Release() - for delegatorIt.Next() { - txIDBytes := delegatorIt.Key() - txID, err := ids.ToID(txIDBytes) - if err != nil { - return err - } - tx, _, err := s.GetTx(txID) - if err != nil { - return err - } - addDelegatorTx, ok := tx.Unsigned.(*txs.AddDelegatorTx) - if !ok { - return fmt.Errorf("expected tx type *txs.AddDelegatorTx but got %T", tx.Unsigned) - } + subnetDelegatorIt := s.pendingSubnetDelegatorList.NewIterator() + defer subnetDelegatorIt.Release() - staker := NewPrimaryNetworkStaker(txID, &addDelegatorTx.Validator) - staker.NextTime = staker.StartTime - staker.Priority = PrimaryNetworkDelegatorPendingPriority - - validator := s.pendingStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) - if validator.delegators == nil { - validator.delegators = btree.New(defaultTreeDegree) - } - validator.delegators.ReplaceOrInsert(staker) + for _, delegatorIt := range []database.Iterator{delegatorIt, subnetDelegatorIt} { + for delegatorIt.Next() { + txIDBytes := delegatorIt.Key() + txID, err := ids.ToID(txIDBytes) + if err != nil { + return err + } + tx, _, err := s.GetTx(txID) + if err != nil { + return err + } - s.pendingStakers.stakers.ReplaceOrInsert(staker) - } - if err := delegatorIt.Error(); err != nil { - return err - } + stakerTx, ok := tx.Unsigned.(txs.Staker) + if !ok { + return fmt.Errorf("expected tx type txs.Staker but got %T", tx.Unsigned) + } - subnetValidatorIt := s.pendingSubnetValidatorList.NewIterator() - defer subnetValidatorIt.Release() - for subnetValidatorIt.Next() { - txIDBytes := subnetValidatorIt.Key() - txID, err := ids.ToID(txIDBytes) - if err != nil { - return err - } - tx, _, err := s.GetTx(txID) - if err != nil { - return err - } + staker := NewPendingStaker(txID, stakerTx) + validator := s.pendingStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) + if validator.delegators == nil { + validator.delegators = btree.New(defaultTreeDegree) + } + validator.delegators.ReplaceOrInsert(staker) - addSubnetValidatorTx, ok := tx.Unsigned.(*txs.AddSubnetValidatorTx) - if !ok { - return fmt.Errorf("expected tx type *txs.AddSubnetValidatorTx but got %T", tx.Unsigned) + s.pendingStakers.stakers.ReplaceOrInsert(staker) } - - staker := NewSubnetStaker(txID, &addSubnetValidatorTx.Validator) - staker.NextTime = staker.StartTime - staker.Priority = SubnetValidatorPendingPriority - - validator := s.pendingStakers.getOrCreateValidator(staker.SubnetID, staker.NodeID) - validator.validator = staker - - s.pendingStakers.stakers.ReplaceOrInsert(staker) } - return subnetValidatorIt.Error() + + errs := wrappers.Errs{} + errs.Add( + validatorIt.Error(), + subnetValidatorIt.Error(), + delegatorIt.Error(), + subnetDelegatorIt.Error(), + ) + return errs.Err } func (s *state) write(height uint64) error { @@ -1169,6 +1291,8 @@ func (s *state) write(height uint64) error { s.writeRewardUTXOs(), s.writeUTXOs(), s.writeSubnets(), + s.writeTransformedSubnets(), + s.writeSubnetSupplies(), s.writeChains(), s.writeMetadata(), ) @@ -1179,10 +1303,12 @@ func (s *state) Close() error { errs := wrappers.Errs{} errs.Add( s.pendingSubnetValidatorBaseDB.Close(), + s.pendingSubnetDelegatorBaseDB.Close(), s.pendingDelegatorBaseDB.Close(), s.pendingValidatorBaseDB.Close(), s.pendingValidatorsDB.Close(), s.currentSubnetValidatorBaseDB.Close(), + s.currentSubnetDelegatorBaseDB.Close(), s.currentDelegatorBaseDB.Close(), s.currentValidatorBaseDB.Close(), s.currentValidatorsDB.Close(), @@ -1191,6 +1317,8 @@ func (s *state) Close() error { s.rewardUTXODB.Close(), s.utxoDB.Close(), s.subnetBaseDB.Close(), + s.transformedSubnetDB.Close(), + s.supplyDB.Close(), s.chainDB.Close(), s.singletonDB.Close(), s.blockDB.Close(), @@ -1232,10 +1360,7 @@ func (s *state) init(genesisBytes []byte) error { // genesisBlock.Accept() because then it'd look for genesisBlock's // non-existent parent) genesisID := hashing.ComputeHash256Array(genesisBytes) - genesisBlock, err := blocks.NewCommitBlock( - genesisID, - 0, - ) + genesisBlock, err := blocks.NewApricotCommitBlock(genesisID, 0 /*height*/) if err != nil { return err } @@ -1295,7 +1420,7 @@ func (s *state) writeBlocks() error { ) // Note: blocks to be stored are verified, so it's safe to marshal them with GenesisCodec - blockBytes, err := blocks.GenesisCodec.Marshal(txs.Version, &stBlk) + blockBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, &stBlk) if err != nil { return fmt.Errorf("failed to marshal block %s to store: %w", blkID, err) } @@ -1356,7 +1481,7 @@ func (s *state) writeCurrentPrimaryNetworkStakers(height uint64) error { Height: height, SubnetID: constants.PrimaryNetworkID, } - prefixBytes, err := genesis.Codec.Marshal(txs.Version, prefixStruct) + prefixBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, prefixStruct) if err != nil { return fmt.Errorf("failed to create prefix bytes: %w", err) } @@ -1389,7 +1514,7 @@ func (s *state) writeCurrentPrimaryNetworkStakers(height uint64) error { PotentialReward: staker.PotentialReward, } - vdrBytes, err := genesis.Codec.Marshal(txs.Version, vdr) + vdrBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, vdr) if err != nil { return fmt.Errorf("failed to serialize current validator: %w", err) } @@ -1402,30 +1527,13 @@ func (s *state) writeCurrentPrimaryNetworkStakers(height uint64) error { } } - addedDelegatorIterator := NewTreeIterator(validatorDiff.addedDelegators) - for addedDelegatorIterator.Next() { - staker := addedDelegatorIterator.Value() - - if err := weightDiff.Add(false, staker.Weight); err != nil { - addedDelegatorIterator.Release() - return fmt.Errorf("failed to increase node weight diff: %w", err) - } - - if err := database.PutUInt64(s.currentDelegatorList, staker.TxID[:], staker.PotentialReward); err != nil { - addedDelegatorIterator.Release() - return fmt.Errorf("failed to write current delegator to list: %w", err) - } - } - addedDelegatorIterator.Release() - - for _, staker := range validatorDiff.deletedDelegators { - if err := weightDiff.Add(true, staker.Weight); err != nil { - return fmt.Errorf("failed to decrease node weight diff: %w", err) - } - - if err := s.currentDelegatorList.Delete(staker.TxID[:]); err != nil { - return fmt.Errorf("failed to delete current staker: %w", err) - } + err := writeCurrentDelegatorDiff( + s.currentDelegatorList, + weightDiff, + validatorDiff, + ) + if err != nil { + return err } if weightDiff.Amount == 0 { @@ -1433,7 +1541,7 @@ func (s *state) writeCurrentPrimaryNetworkStakers(height uint64) error { } weightDiffs[nodeID] = weightDiff - weightDiffBytes, err := genesis.Codec.Marshal(txs.Version, weightDiff) + weightDiffBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, weightDiff) if err != nil { return fmt.Errorf("failed to serialize validator weight diff: %w", err) } @@ -1457,6 +1565,14 @@ func (s *state) writeCurrentPrimaryNetworkStakers(height uint64) error { } s.validatorDiffsCache.Put(string(prefixBytes), weightDiffs) + // Handle default validators + for _, v := range validators.ExpiredDefaultValidators(s.GetNetworkID(), s.GetTimestamp()) { + err := s.cfg.Validators.RemoveWeight(constants.PrimaryNetworkID, v.ID(), v.Weight()) + if err != nil { + return fmt.Errorf("failed to remove default validator weight: %w", err) + } + } + // TODO: Move validator set management out of the state package // // Attempt to update the stake metrics @@ -1484,7 +1600,7 @@ func (s *state) writeCurrentSubnetStakers(height uint64) error { Height: height, SubnetID: subnetID, } - prefixBytes, err := genesis.Codec.Marshal(txs.Version, prefixStruct) + prefixBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, prefixStruct) if err != nil { return fmt.Errorf("failed to create prefix bytes: %w", err) } @@ -1503,21 +1619,28 @@ func (s *state) writeCurrentSubnetStakers(height uint64) error { if validatorDiff.validatorDeleted { err = s.currentSubnetValidatorList.Delete(staker.TxID[:]) } else { - err = s.currentSubnetValidatorList.Put(staker.TxID[:], nil) + err = database.PutUInt64(s.currentSubnetValidatorList, staker.TxID[:], staker.PotentialReward) } if err != nil { return fmt.Errorf("failed to update current subnet staker: %w", err) } } - // TODO: manage subnet delegators here + err := writeCurrentDelegatorDiff( + s.currentSubnetDelegatorList, + weightDiff, + validatorDiff, + ) + if err != nil { + return err + } if weightDiff.Amount == 0 { continue } weightDiffs[nodeID] = weightDiff - weightDiffBytes, err := genesis.Codec.Marshal(txs.Version, weightDiff) + weightDiffBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, weightDiff) if err != nil { return fmt.Errorf("failed to serialize validator weight diff: %w", err) } @@ -1546,37 +1669,46 @@ func (s *state) writeCurrentSubnetStakers(height uint64) error { return nil } -func (s *state) writePendingPrimaryNetworkStakers() error { - for _, validatorDiff := range s.pendingStakers.validatorDiffs[constants.PrimaryNetworkID] { - if validatorDiff.validatorModified { - staker := validatorDiff.validator +func writeCurrentDelegatorDiff( + currentDelegatorList linkeddb.LinkedDB, + weightDiff *ValidatorWeightDiff, + validatorDiff *diffValidator, +) error { + addedDelegatorIterator := NewTreeIterator(validatorDiff.addedDelegators) + defer addedDelegatorIterator.Release() + for addedDelegatorIterator.Next() { + staker := addedDelegatorIterator.Value() - var err error - if validatorDiff.validatorDeleted { - err = s.pendingValidatorList.Delete(staker.TxID[:]) - } else { - err = s.pendingValidatorList.Put(staker.TxID[:], nil) - } - if err != nil { - return fmt.Errorf("failed to update pending primary network staker: %w", err) - } + if err := weightDiff.Add(false, staker.Weight); err != nil { + return fmt.Errorf("failed to increase node weight diff: %w", err) } - addedDelegatorIterator := NewTreeIterator(validatorDiff.addedDelegators) - for addedDelegatorIterator.Next() { - staker := addedDelegatorIterator.Value() + if err := database.PutUInt64(currentDelegatorList, staker.TxID[:], staker.PotentialReward); err != nil { + return fmt.Errorf("failed to write current delegator to list: %w", err) + } + } - if err := s.pendingDelegatorList.Put(staker.TxID[:], nil); err != nil { - addedDelegatorIterator.Release() - return fmt.Errorf("failed to write pending delegator to list: %w", err) - } + for _, staker := range validatorDiff.deletedDelegators { + if err := weightDiff.Add(true, staker.Weight); err != nil { + return fmt.Errorf("failed to decrease node weight diff: %w", err) } - addedDelegatorIterator.Release() - for _, staker := range validatorDiff.deletedDelegators { - if err := s.pendingDelegatorList.Delete(staker.TxID[:]); err != nil { - return fmt.Errorf("failed to delete pending delegator: %w", err) - } + if err := currentDelegatorList.Delete(staker.TxID[:]); err != nil { + return fmt.Errorf("failed to delete current staker: %w", err) + } + } + return nil +} + +func (s *state) writePendingPrimaryNetworkStakers() error { + for _, validatorDiff := range s.pendingStakers.validatorDiffs[constants.PrimaryNetworkID] { + err := writePendingDiff( + s.pendingValidatorList, + s.pendingDelegatorList, + validatorDiff, + ) + if err != nil { + return err } } return nil @@ -1593,21 +1725,51 @@ func (s *state) writePendingSubnetStakers() error { } for _, validatorDiff := range subnetValidatorDiffs { - if validatorDiff.validatorModified { - staker := validatorDiff.validator - - var err error - if validatorDiff.validatorDeleted { - err = s.pendingSubnetValidatorList.Delete(staker.TxID[:]) - } else { - err = s.pendingSubnetValidatorList.Put(staker.TxID[:], nil) - } - if err != nil { - return fmt.Errorf("failed to update pending subnet staker: %w", err) - } + err := writePendingDiff( + s.pendingSubnetValidatorList, + s.pendingSubnetDelegatorList, + validatorDiff, + ) + if err != nil { + return err } + } + } + return nil +} - // TODO: manage subnet delegators here +func writePendingDiff( + pendingValidatorList linkeddb.LinkedDB, + pendingDelegatorList linkeddb.LinkedDB, + validatorDiff *diffValidator, +) error { + if validatorDiff.validatorModified { + staker := validatorDiff.validator + + var err error + if validatorDiff.validatorDeleted { + err = pendingValidatorList.Delete(staker.TxID[:]) + } else { + err = pendingValidatorList.Put(staker.TxID[:], nil) + } + if err != nil { + return fmt.Errorf("failed to update pending validator: %w", err) + } + } + + addedDelegatorIterator := NewTreeIterator(validatorDiff.addedDelegators) + defer addedDelegatorIterator.Release() + for addedDelegatorIterator.Next() { + staker := addedDelegatorIterator.Value() + + if err := pendingDelegatorList.Put(staker.TxID[:], nil); err != nil { + return fmt.Errorf("failed to write pending delegator to list: %w", err) + } + } + + for _, staker := range validatorDiff.deletedDelegators { + if err := pendingDelegatorList.Delete(staker.TxID[:]); err != nil { + return fmt.Errorf("failed to delete pending delegator: %w", err) } } return nil @@ -1617,7 +1779,7 @@ func (s *state) writeUptimes() error { for nodeID := range s.updatedUptimes { delete(s.updatedUptimes, nodeID) - // SGB-MERGE skip default validator + // skip default validator if validators.IsDefaultValidator(nodeID) { continue } @@ -1625,7 +1787,7 @@ func (s *state) writeUptimes() error { uptime := s.uptimes[nodeID] uptime.LastUpdated = uint64(uptime.lastUpdated.Unix()) - uptimeBytes, err := genesis.Codec.Marshal(txs.Version, uptime) + uptimeBytes, err := blocks.GenesisCodec.Marshal(blocks.Version, uptime) if err != nil { return fmt.Errorf("failed to serialize uptime: %w", err) } @@ -1646,7 +1808,9 @@ func (s *state) writeTXs() error { Status: txStatus.status, } - txBytes, err := genesis.Codec.Marshal(txs.Version, &stx) + // Note that we're serializing a [txBytesAndStatus] here, not a + // *txs.Tx, so we don't use [txs.Codec]. + txBytes, err := txs.GenesisCodec.Marshal(txs.Version, &stx) if err != nil { return fmt.Errorf("failed to serialize tx: %w", err) } @@ -1668,7 +1832,7 @@ func (s *state) writeRewardUTXOs() error { txDB := linkeddb.NewDefault(rawTxDB) for _, utxo := range utxos { - utxoBytes, err := genesis.Codec.Marshal(txs.Version, utxo) + utxoBytes, err := txs.GenesisCodec.Marshal(txs.Version, utxo) if err != nil { return fmt.Errorf("failed to serialize reward UTXO: %w", err) } @@ -1710,6 +1874,30 @@ func (s *state) writeSubnets() error { return nil } +func (s *state) writeTransformedSubnets() error { + for subnetID, tx := range s.transformedSubnets { + txID := tx.ID() + + delete(s.transformedSubnets, subnetID) + s.transformedSubnetCache.Put(subnetID, tx) + if err := database.PutID(s.transformedSubnetDB, subnetID[:], txID); err != nil { + return fmt.Errorf("failed to write transformed subnet: %w", err) + } + } + return nil +} + +func (s *state) writeSubnetSupplies() error { + for subnetID, supply := range s.modifiedSupplies { + delete(s.modifiedSupplies, subnetID) + s.supplyCache.Put(subnetID, supply) + if err := database.PutUInt64(s.supplyDB, subnetID[:], supply); err != nil { + return fmt.Errorf("failed to write subnet supply: %w", err) + } + } + return nil +} + func (s *state) writeChains() error { for subnetID, chains := range s.addedChains { for _, chain := range chains { diff --git a/avalanchego/vms/platformvm/state/state_test.go b/avalanchego/vms/platformvm/state/state_test.go index dd48c94e..17aa5d42 100644 --- a/avalanchego/vms/platformvm/state/state_test.go +++ b/avalanchego/vms/platformvm/state/state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -263,7 +263,7 @@ func newInitializedState(require *require.Assertions) (State, database.Database) End: uint64(initialValidatorEndTime.Unix()), Wght: units.Avax, }, - Stake: []*avax.TransferableOutput{ + StakeOuts: []*avax.TransferableOutput{ { Asset: avax.Asset{ID: initialTxID}, Out: &secp256k1fx.TransferOutput{ @@ -271,8 +271,8 @@ func newInitializedState(require *require.Assertions) (State, database.Database) }, }, }, - RewardsOwner: &secp256k1fx.OutputOwners{}, - Shares: reward.PercentDenominator, + RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationShares: reward.PercentDenominator, } initialValidatorTx := &txs.Tx{Unsigned: initialValidator} require.NoError(initialValidatorTx.Sign(txs.Codec, nil)) @@ -310,7 +310,7 @@ func newInitializedState(require *require.Assertions) (State, database.Database) InitialSupply: units.Schmeckle + units.Avax, } - genesisBlk, err := blocks.NewCommitBlock(genesisBlkID, 0) + genesisBlk, err := blocks.NewApricotCommitBlock(genesisBlkID, 0) require.NoError(err) require.NoError(s.(*state).syncGenesis(genesisBlk, genesisState)) diff --git a/avalanchego/vms/platformvm/state/tree_iterator.go b/avalanchego/vms/platformvm/state/tree_iterator.go index bf6fdcb2..315cd62c 100644 --- a/avalanchego/vms/platformvm/state/tree_iterator.go +++ b/avalanchego/vms/platformvm/state/tree_iterator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -22,7 +22,7 @@ type treeIterator struct { } // NewTreeIterator returns a new iterator of the stakers in [tree] in ascending -// order. +// order. Note that it isn't safe to modify [tree] while iterating over it. func NewTreeIterator(tree *btree.BTree) StakerIterator { if tree == nil { return EmptyIterator diff --git a/avalanchego/vms/platformvm/state/tree_iterator_test.go b/avalanchego/vms/platformvm/state/tree_iterator_test.go index 8eedc60b..16b8b2ee 100644 --- a/avalanchego/vms/platformvm/state/tree_iterator_test.go +++ b/avalanchego/vms/platformvm/state/tree_iterator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/utxos.go b/avalanchego/vms/platformvm/state/utxos.go index 010a7789..e9ccff30 100644 --- a/avalanchego/vms/platformvm/state/utxos.go +++ b/avalanchego/vms/platformvm/state/utxos.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/state/versions.go b/avalanchego/vms/platformvm/state/versions.go index 33dc3640..3668da30 100644 --- a/avalanchego/vms/platformvm/state/versions.go +++ b/avalanchego/vms/platformvm/state/versions.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/platformvm/status/blockchain_status.go b/avalanchego/vms/platformvm/status/blockchain_status.go index 01137009..6f878ab4 100644 --- a/avalanchego/vms/platformvm/status/blockchain_status.go +++ b/avalanchego/vms/platformvm/status/blockchain_status.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package status diff --git a/avalanchego/vms/platformvm/status/blockchain_status_test.go b/avalanchego/vms/platformvm/status/blockchain_status_test.go index 35031f7c..ce76b266 100644 --- a/avalanchego/vms/platformvm/status/blockchain_status_test.go +++ b/avalanchego/vms/platformvm/status/blockchain_status_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package status diff --git a/avalanchego/vms/platformvm/status/status.go b/avalanchego/vms/platformvm/status/status.go index cef184c6..8af00df4 100644 --- a/avalanchego/vms/platformvm/status/status.go +++ b/avalanchego/vms/platformvm/status/status.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package status diff --git a/avalanchego/vms/platformvm/status/status_test.go b/avalanchego/vms/platformvm/status/status_test.go index a12bbc77..0b9e4066 100644 --- a/avalanchego/vms/platformvm/status/status_test.go +++ b/avalanchego/vms/platformvm/status/status_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package status diff --git a/avalanchego/vms/platformvm/txs/add_delegator_test.go b/avalanchego/vms/platformvm/txs/add_delegator_test.go index 71058a1a..fdf5f2f9 100644 --- a/avalanchego/vms/platformvm/txs/add_delegator_test.go +++ b/avalanchego/vms/platformvm/txs/add_delegator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -35,7 +35,7 @@ func TestAddDelegatorTxSyntacticVerify(t *testing.T) { ) // Case : signed tx is nil - require.ErrorIs(stx.SyntacticVerify(ctx), errNilSignedTx) + require.ErrorIs(stx.SyntacticVerify(ctx), ErrNilSignedTx) // Case : unsigned tx is nil require.ErrorIs(addDelegatorTx.SyntacticVerify(ctx), ErrNilTx) @@ -89,8 +89,8 @@ func TestAddDelegatorTxSyntacticVerify(t *testing.T) { End: uint64(clk.Time().Add(time.Hour).Unix()), Wght: validatorWeight, }, - Stake: stakes, - RewardsOwner: &secp256k1fx.OutputOwners{ + StakeOuts: stakes, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, @@ -187,8 +187,8 @@ func TestAddDelegatorTxSyntacticVerifyNotAVAX(t *testing.T) { End: uint64(clk.Time().Add(time.Hour).Unix()), Wght: validatorWeight, }, - Stake: stakes, - RewardsOwner: &secp256k1fx.OutputOwners{ + StakeOuts: stakes, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, @@ -199,3 +199,9 @@ func TestAddDelegatorTxSyntacticVerifyNotAVAX(t *testing.T) { require.NoError(err) require.Error(stx.SyntacticVerify(ctx)) } + +func TestAddDelegatorTxNotValidatorTx(t *testing.T) { + txIntf := any((*AddDelegatorTx)(nil)) + _, ok := txIntf.(ValidatorTx) + require.False(t, ok) +} diff --git a/avalanchego/vms/platformvm/txs/add_delegator_tx.go b/avalanchego/vms/platformvm/txs/add_delegator_tx.go index 3f2e58b7..ae206a8f 100644 --- a/avalanchego/vms/platformvm/txs/add_delegator_tx.go +++ b/avalanchego/vms/platformvm/txs/add_delegator_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -8,7 +8,9 @@ import ( "fmt" "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/verify" @@ -18,9 +20,7 @@ import ( ) var ( - _ UnsignedTx = &AddDelegatorTx{} - _ StakerTx = &AddDelegatorTx{} - _ secp256k1fx.UnsignedTx = &AddDelegatorTx{} + _ DelegatorTx = &AddDelegatorTx{} errDelegatorWeightMismatch = errors.New("delegator weight is not equal to total stake weight") ) @@ -32,9 +32,9 @@ type AddDelegatorTx struct { // Describes the delegatee Validator validator.Validator `serialize:"true" json:"validator"` // Where to send staked tokens when done validating - Stake []*avax.TransferableOutput `serialize:"true" json:"stake"` + StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"` // Where to send staking rewards when done validating - RewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"` + DelegationRewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"` } // InitCtx sets the FxID fields in the inputs and outputs of this @@ -42,27 +42,24 @@ type AddDelegatorTx struct { // the addresses can be json marshalled into human readable format func (tx *AddDelegatorTx) InitCtx(ctx *snow.Context) { tx.BaseTx.InitCtx(ctx) - for _, out := range tx.Stake { + for _, out := range tx.StakeOuts { out.FxID = secp256k1fx.ID out.InitCtx(ctx) } - tx.RewardsOwner.InitCtx(ctx) + tx.DelegationRewardsOwner.InitCtx(ctx) } -// StartTime of this validator -func (tx *AddDelegatorTx) StartTime() time.Time { - return tx.Validator.StartTime() -} - -// EndTime of this validator -func (tx *AddDelegatorTx) EndTime() time.Time { - return tx.Validator.EndTime() -} - -// Weight of this validator -func (tx *AddDelegatorTx) Weight() uint64 { - return tx.Validator.Weight() +func (tx *AddDelegatorTx) SubnetID() ids.ID { return constants.PrimaryNetworkID } +func (tx *AddDelegatorTx) NodeID() ids.NodeID { return tx.Validator.NodeID } +func (tx *AddDelegatorTx) StartTime() time.Time { return tx.Validator.StartTime() } +func (tx *AddDelegatorTx) EndTime() time.Time { return tx.Validator.EndTime() } +func (tx *AddDelegatorTx) Weight() uint64 { return tx.Validator.Wght } +func (tx *AddDelegatorTx) PendingPriority() Priority { + return PrimaryNetworkDelegatorApricotPendingPriority } +func (tx *AddDelegatorTx) CurrentPriority() Priority { return PrimaryNetworkDelegatorCurrentPriority } +func (tx *AddDelegatorTx) Stake() []*avax.TransferableOutput { return tx.StakeOuts } +func (tx *AddDelegatorTx) RewardsOwner() fx.Owner { return tx.DelegationRewardsOwner } // SyntacticVerify returns nil iff [tx] is valid func (tx *AddDelegatorTx) SyntacticVerify(ctx *snow.Context) error { @@ -76,12 +73,12 @@ func (tx *AddDelegatorTx) SyntacticVerify(ctx *snow.Context) error { if err := tx.BaseTx.SyntacticVerify(ctx); err != nil { return err } - if err := verify.All(&tx.Validator, tx.RewardsOwner); err != nil { + if err := verify.All(&tx.Validator, tx.DelegationRewardsOwner); err != nil { return fmt.Errorf("failed to verify validator or rewards owner: %w", err) } totalStakeWeight := uint64(0) - for _, out := range tx.Stake { + for _, out := range tx.StakeOuts { if err := out.Verify(); err != nil { return fmt.Errorf("output verification failed: %w", err) } @@ -98,7 +95,7 @@ func (tx *AddDelegatorTx) SyntacticVerify(ctx *snow.Context) error { } switch { - case !avax.IsSortedTransferableOutputs(tx.Stake, Codec): + case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec): return errOutputsNotSorted case totalStakeWeight != tx.Validator.Wght: return fmt.Errorf("%w, delegator weight %d total stake weight %d", diff --git a/avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx.go b/avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx.go new file mode 100644 index 00000000..eadd23ee --- /dev/null +++ b/avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx.go @@ -0,0 +1,135 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "fmt" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +var _ DelegatorTx = &AddPermissionlessDelegatorTx{} + +// AddPermissionlessDelegatorTx is an unsigned addPermissionlessDelegatorTx +type AddPermissionlessDelegatorTx struct { + // Metadata, inputs and outputs + BaseTx `serialize:"true"` + // Describes the validator + Validator validator.Validator `serialize:"true" json:"validator"` + // ID of the subnet this validator is validating + Subnet ids.ID `serialize:"true" json:"subnetID"` + // Where to send staked tokens when done validating + StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"` + // Where to send staking rewards when done validating + DelegationRewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"` +} + +// InitCtx sets the FxID fields in the inputs and outputs of this +// [AddPermissionlessDelegatorTx]. Also sets the [ctx] to the given [vm.ctx] so +// that the addresses can be json marshalled into human readable format +func (tx *AddPermissionlessDelegatorTx) InitCtx(ctx *snow.Context) { + tx.BaseTx.InitCtx(ctx) + for _, out := range tx.StakeOuts { + out.FxID = secp256k1fx.ID + out.InitCtx(ctx) + } + tx.DelegationRewardsOwner.InitCtx(ctx) +} + +func (tx *AddPermissionlessDelegatorTx) SubnetID() ids.ID { return tx.Subnet } +func (tx *AddPermissionlessDelegatorTx) NodeID() ids.NodeID { return tx.Validator.NodeID } +func (tx *AddPermissionlessDelegatorTx) StartTime() time.Time { return tx.Validator.StartTime() } +func (tx *AddPermissionlessDelegatorTx) EndTime() time.Time { return tx.Validator.EndTime() } +func (tx *AddPermissionlessDelegatorTx) Weight() uint64 { return tx.Validator.Wght } + +func (tx *AddPermissionlessDelegatorTx) PendingPriority() Priority { + if tx.Subnet == constants.PrimaryNetworkID { + return PrimaryNetworkDelegatorBanffPendingPriority + } + return SubnetPermissionlessDelegatorPendingPriority +} + +func (tx *AddPermissionlessDelegatorTx) CurrentPriority() Priority { + if tx.Subnet == constants.PrimaryNetworkID { + return PrimaryNetworkDelegatorCurrentPriority + } + return SubnetPermissionlessDelegatorCurrentPriority +} + +func (tx *AddPermissionlessDelegatorTx) Stake() []*avax.TransferableOutput { + return tx.StakeOuts +} + +func (tx *AddPermissionlessDelegatorTx) RewardsOwner() fx.Owner { + return tx.DelegationRewardsOwner +} + +// SyntacticVerify returns nil iff [tx] is valid +func (tx *AddPermissionlessDelegatorTx) SyntacticVerify(ctx *snow.Context) error { + switch { + case tx == nil: + return ErrNilTx + case tx.SyntacticallyVerified: // already passed syntactic verification + return nil + case len(tx.StakeOuts) == 0: // Ensure there is provided stake + return errNoStake + } + + if err := tx.BaseTx.SyntacticVerify(ctx); err != nil { + return fmt.Errorf("failed to verify BaseTx: %w", err) + } + if err := verify.All(&tx.Validator, tx.DelegationRewardsOwner); err != nil { + return fmt.Errorf("failed to verify validator or rewards owner: %w", err) + } + + for _, out := range tx.StakeOuts { + if err := out.Verify(); err != nil { + return fmt.Errorf("failed to verify output: %w", err) + } + } + + firstStakeOutput := tx.StakeOuts[0] + stakedAssetID := firstStakeOutput.AssetID() + totalStakeWeight := firstStakeOutput.Output().Amount() + for _, out := range tx.StakeOuts[1:] { + newWeight, err := math.Add64(totalStakeWeight, out.Output().Amount()) + if err != nil { + return err + } + totalStakeWeight = newWeight + + assetID := out.AssetID() + if assetID != stakedAssetID { + return fmt.Errorf("%w: %q and %q", errMultipleStakedAssets, stakedAssetID, assetID) + } + } + + switch { + case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec): + return errOutputsNotSorted + case totalStakeWeight != tx.Validator.Wght: + return fmt.Errorf("%w, delegator weight %d total stake weight %d", + errDelegatorWeightMismatch, + tx.Validator.Wght, + totalStakeWeight, + ) + } + + // cache that this is valid + tx.SyntacticallyVerified = true + return nil +} + +func (tx *AddPermissionlessDelegatorTx) Visit(visitor Visitor) error { + return visitor.AddPermissionlessDelegatorTx(tx) +} diff --git a/avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx_test.go b/avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx_test.go new file mode 100644 index 00000000..e921b585 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/add_permissionless_delegator_tx_test.go @@ -0,0 +1,393 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "errors" + "math" + "testing" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +func TestAddPermissionlessDelegatorTxSyntacticVerify(t *testing.T) { + type test struct { + name string + txFunc func(*gomock.Controller) *AddPermissionlessDelegatorTx + err error + } + + var ( + networkID = uint32(1337) + chainID = ids.GenerateTestID() + ) + + ctx := &snow.Context{ + ChainID: chainID, + NetworkID: networkID, + } + + // A BaseTx that already passed syntactic verification. + verifiedBaseTx := BaseTx{ + SyntacticallyVerified: true, + } + + // A BaseTx that passes syntactic verification. + validBaseTx := BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: networkID, + BlockchainID: chainID, + }, + } + + // A BaseTx that fails syntactic verification. + invalidBaseTx := BaseTx{} + + errCustom := errors.New("custom error") + + tests := []test{ + { + name: "nil tx", + txFunc: func(*gomock.Controller) *AddPermissionlessDelegatorTx { + return nil + }, + err: ErrNilTx, + }, + { + name: "already verified", + txFunc: func(*gomock.Controller) *AddPermissionlessDelegatorTx { + return &AddPermissionlessDelegatorTx{ + BaseTx: verifiedBaseTx, + } + }, + err: nil, + }, + { + name: "no provided stake", + txFunc: func(*gomock.Controller) *AddPermissionlessDelegatorTx { + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + StakeOuts: nil, + } + }, + err: errNoStake, + }, + { + name: "invalid rewards owner", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(errCustom) + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + }, + err: errCustom, + }, + { + name: "invalid stake output", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + + stakeOut := avax.NewMockTransferableOut(ctrl) + stakeOut.EXPECT().Verify().Return(errCustom) + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: stakeOut, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + }, + err: errCustom, + }, + { + name: "multiple staked assets", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + }, + err: errMultipleStakedAssets, + }, + { + name: "stake not sorted", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 2, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + }, + err: errOutputsNotSorted, + }, + { + name: "weight mismatch", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + }, + err: errDelegatorWeightMismatch, + }, + { + name: "valid subnet validator", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + Wght: 2, + }, + Subnet: ids.GenerateTestID(), + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + }, + err: nil, + }, + { + name: "valid primary network validator", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + Wght: 2, + }, + Subnet: constants.PrimaryNetworkID, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + }, + err: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + tx := tt.txFunc(ctrl) + err := tx.SyntacticVerify(ctx) + require.ErrorIs(err, tt.err) + }) + } + + t.Run("invalid BaseTx", func(t *testing.T) { + require := require.New(t) + tx := &AddPermissionlessDelegatorTx{ + BaseTx: invalidBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + }, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + } + err := tx.SyntacticVerify(ctx) + require.Error(err) + }) + + t.Run("stake overflow", func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + tx := &AddPermissionlessDelegatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: math.MaxUint64, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 2, + }, + }, + }, + DelegationRewardsOwner: rewardsOwner, + } + err := tx.SyntacticVerify(ctx) + require.Error(err) + }) +} + +func TestAddPermissionlessDelegatorTxNotValidatorTx(t *testing.T) { + txIntf := any((*AddPermissionlessDelegatorTx)(nil)) + _, ok := txIntf.(ValidatorTx) + require.False(t, ok) +} diff --git a/avalanchego/vms/platformvm/txs/add_permissionless_validator_tx.go b/avalanchego/vms/platformvm/txs/add_permissionless_validator_tx.go new file mode 100644 index 00000000..33fb6396 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/add_permissionless_validator_tx.go @@ -0,0 +1,179 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "errors" + "fmt" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +var ( + _ ValidatorTx = &AddPermissionlessValidatorTx{} + + errEmptyNodeID = errors.New("validator nodeID cannot be empty") + errNoStake = errors.New("no stake") + errInvalidSigner = errors.New("invalid signer") + errMultipleStakedAssets = errors.New("multiple staked assets") + errValidatorWeightMismatch = errors.New("validator weight mismatch") +) + +// AddPermissionlessValidatorTx is an unsigned addPermissionlessValidatorTx +type AddPermissionlessValidatorTx struct { + // Metadata, inputs and outputs + BaseTx `serialize:"true"` + // Describes the validator + Validator validator.Validator `serialize:"true" json:"validator"` + // ID of the subnet this validator is validating + Subnet ids.ID `serialize:"true" json:"subnetID"` + // If the [Subnet] is the primary network, [Signer] is the BLS key for this + // validator. If the [Subnet] is not the primary network, this value is the + // empty signer + // Note: We do not enforce that the BLS key is unique across all validators. + // This means that validators can share a key if they so choose. + // However, a NodeID does uniquely map to a BLS key + Signer signer.Signer `serialize:"true" json:"signer"` + // Where to send staked tokens when done validating + StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"` + // Where to send validation rewards when done validating + ValidatorRewardsOwner fx.Owner `serialize:"true" json:"validationRewardsOwner"` + // Where to send delegation rewards when done validating + DelegatorRewardsOwner fx.Owner `serialize:"true" json:"delegationRewardsOwner"` + // Fee this validator charges delegators as a percentage, times 10,000 + // For example, if this validator has DelegationShares=300,000 then they + // take 30% of rewards from delegators + DelegationShares uint32 `serialize:"true" json:"shares"` +} + +// InitCtx sets the FxID fields in the inputs and outputs of this +// [AddPermissionlessValidatorTx]. Also sets the [ctx] to the given [vm.ctx] so +// that the addresses can be json marshalled into human readable format +func (tx *AddPermissionlessValidatorTx) InitCtx(ctx *snow.Context) { + tx.BaseTx.InitCtx(ctx) + for _, out := range tx.StakeOuts { + out.FxID = secp256k1fx.ID + out.InitCtx(ctx) + } + tx.ValidatorRewardsOwner.InitCtx(ctx) + tx.DelegatorRewardsOwner.InitCtx(ctx) +} + +func (tx *AddPermissionlessValidatorTx) SubnetID() ids.ID { return tx.Subnet } +func (tx *AddPermissionlessValidatorTx) NodeID() ids.NodeID { return tx.Validator.NodeID } +func (tx *AddPermissionlessValidatorTx) StartTime() time.Time { return tx.Validator.StartTime() } +func (tx *AddPermissionlessValidatorTx) EndTime() time.Time { return tx.Validator.EndTime() } +func (tx *AddPermissionlessValidatorTx) Weight() uint64 { return tx.Validator.Wght } + +func (tx *AddPermissionlessValidatorTx) PendingPriority() Priority { + if tx.Subnet == constants.PrimaryNetworkID { + return PrimaryNetworkValidatorPendingPriority + } + return SubnetPermissionlessValidatorPendingPriority +} + +func (tx *AddPermissionlessValidatorTx) CurrentPriority() Priority { + if tx.Subnet == constants.PrimaryNetworkID { + return PrimaryNetworkValidatorCurrentPriority + } + return SubnetPermissionlessValidatorCurrentPriority +} + +func (tx *AddPermissionlessValidatorTx) Stake() []*avax.TransferableOutput { + return tx.StakeOuts +} + +func (tx *AddPermissionlessValidatorTx) ValidationRewardsOwner() fx.Owner { + return tx.ValidatorRewardsOwner +} + +func (tx *AddPermissionlessValidatorTx) DelegationRewardsOwner() fx.Owner { + return tx.DelegatorRewardsOwner +} + +func (tx *AddPermissionlessValidatorTx) Shares() uint32 { + return tx.DelegationShares +} + +// SyntacticVerify returns nil iff [tx] is valid +func (tx *AddPermissionlessValidatorTx) SyntacticVerify(ctx *snow.Context) error { + switch { + case tx == nil: + return ErrNilTx + case tx.SyntacticallyVerified: // already passed syntactic verification + return nil + case tx.Validator.NodeID == ids.EmptyNodeID: + return errEmptyNodeID + case len(tx.StakeOuts) == 0: // Ensure there is provided stake + return errNoStake + case tx.DelegationShares > reward.PercentDenominator: + return errTooManyShares + } + + if err := tx.BaseTx.SyntacticVerify(ctx); err != nil { + return fmt.Errorf("failed to verify BaseTx: %w", err) + } + if err := verify.All(&tx.Validator, tx.Signer, tx.ValidatorRewardsOwner, tx.DelegatorRewardsOwner); err != nil { + return fmt.Errorf("failed to verify validator, signer, or rewards owners: %w", err) + } + + hasKey := tx.Signer.Key() != nil + isPrimaryNetwork := tx.Subnet == constants.PrimaryNetworkID + if hasKey != isPrimaryNetwork { + return fmt.Errorf( + "%w: hasKey=%v != isPrimaryNetwork=%v", + errInvalidSigner, + hasKey, + isPrimaryNetwork, + ) + } + + for _, out := range tx.StakeOuts { + if err := out.Verify(); err != nil { + return fmt.Errorf("failed to verify output: %w", err) + } + } + + firstStakeOutput := tx.StakeOuts[0] + stakedAssetID := firstStakeOutput.AssetID() + totalStakeWeight := firstStakeOutput.Output().Amount() + for _, out := range tx.StakeOuts[1:] { + newWeight, err := math.Add64(totalStakeWeight, out.Output().Amount()) + if err != nil { + return err + } + totalStakeWeight = newWeight + + assetID := out.AssetID() + if assetID != stakedAssetID { + return fmt.Errorf("%w: %q and %q", errMultipleStakedAssets, stakedAssetID, assetID) + } + } + + switch { + case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec): + return errOutputsNotSorted + case totalStakeWeight != tx.Validator.Wght: + return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, tx.Validator.Wght, totalStakeWeight) + } + + // cache that this is valid + tx.SyntacticallyVerified = true + return nil +} + +func (tx *AddPermissionlessValidatorTx) Visit(visitor Visitor) error { + return visitor.AddPermissionlessValidatorTx(tx) +} diff --git a/avalanchego/vms/platformvm/txs/add_permissionless_validator_tx_test.go b/avalanchego/vms/platformvm/txs/add_permissionless_validator_tx_test.go new file mode 100644 index 00000000..41071f74 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/add_permissionless_validator_tx_test.go @@ -0,0 +1,501 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "errors" + "math" + "testing" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +func TestAddPermissionlessValidatorTxSyntacticVerify(t *testing.T) { + type test struct { + name string + txFunc func(*gomock.Controller) *AddPermissionlessValidatorTx + err error + } + + var ( + networkID = uint32(1337) + chainID = ids.GenerateTestID() + ) + + ctx := &snow.Context{ + ChainID: chainID, + NetworkID: networkID, + } + + // A BaseTx that already passed syntactic verification. + verifiedBaseTx := BaseTx{ + SyntacticallyVerified: true, + } + + // A BaseTx that passes syntactic verification. + validBaseTx := BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: networkID, + BlockchainID: chainID, + }, + } + + blsSK, err := bls.NewSecretKey() + require.NoError(t, err) + + blsPOP := signer.NewProofOfPossession(blsSK) + + // A BaseTx that fails syntactic verification. + invalidBaseTx := BaseTx{} + + errCustom := errors.New("custom error") + + tests := []test{ + { + name: "nil tx", + txFunc: func(*gomock.Controller) *AddPermissionlessValidatorTx { + return nil + }, + err: ErrNilTx, + }, + { + name: "already verified", + txFunc: func(*gomock.Controller) *AddPermissionlessValidatorTx { + return &AddPermissionlessValidatorTx{ + BaseTx: verifiedBaseTx, + } + }, + err: nil, + }, + { + name: "empty nodeID", + txFunc: func(*gomock.Controller) *AddPermissionlessValidatorTx { + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.EmptyNodeID, + }, + } + }, + err: errEmptyNodeID, + }, + { + name: "no provided stake", + txFunc: func(*gomock.Controller) *AddPermissionlessValidatorTx { + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + }, + StakeOuts: nil, + } + }, + err: errNoStake, + }, + { + name: "too many shares", + txFunc: func(*gomock.Controller) *AddPermissionlessValidatorTx { + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + }, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationShares: reward.PercentDenominator + 1, + } + }, + err: errTooManyShares, + }, + { + name: "invalid rewards owner", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(errCustom) + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: errCustom, + }, + { + name: "wrong signer", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: constants.PrimaryNetworkID, + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: errInvalidSigner, + }, + { + name: "invalid stake output", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + + stakeOut := avax.NewMockTransferableOut(ctrl) + stakeOut.EXPECT().Verify().Return(errCustom) + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: stakeOut, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: errCustom, + }, + { + name: "multiple staked assets", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: errMultipleStakedAssets, + }, + { + name: "stake not sorted", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 2, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: errOutputsNotSorted, + }, + { + name: "weight mismatch", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: errValidatorWeightMismatch, + }, + { + name: "valid subnet validator", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 2, + }, + Subnet: ids.GenerateTestID(), + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: nil, + }, + { + name: "valid primary network validator", + txFunc: func(ctrl *gomock.Controller) *AddPermissionlessValidatorTx { + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + return &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 2, + }, + Subnet: constants.PrimaryNetworkID, + Signer: blsPOP, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + }, + err: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + tx := tt.txFunc(ctrl) + err := tx.SyntacticVerify(ctx) + require.ErrorIs(err, tt.err) + }) + } + + t.Run("invalid BaseTx", func(t *testing.T) { + require := require.New(t) + tx := &AddPermissionlessValidatorTx{ + BaseTx: invalidBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + }, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + }, + }, + }, + DelegationShares: reward.PercentDenominator, + } + err := tx.SyntacticVerify(ctx) + require.Error(err) + }) + + t.Run("stake overflow", func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rewardsOwner := fx.NewMockOwner(ctrl) + rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes() + assetID := ids.GenerateTestID() + tx := &AddPermissionlessValidatorTx{ + BaseTx: validBaseTx, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Wght: 1, + }, + Subnet: ids.GenerateTestID(), + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: math.MaxUint64, + }, + }, + { + Asset: avax.Asset{ + ID: assetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 2, + }, + }, + }, + ValidatorRewardsOwner: rewardsOwner, + DelegatorRewardsOwner: rewardsOwner, + DelegationShares: reward.PercentDenominator, + } + err := tx.SyntacticVerify(ctx) + require.Error(err) + }) +} + +func TestAddPermissionlessValidatorTxNotDelegatorTx(t *testing.T) { + txIntf := any((*AddPermissionlessValidatorTx)(nil)) + _, ok := txIntf.(DelegatorTx) + require.False(t, ok) +} diff --git a/avalanchego/vms/platformvm/txs/add_subnet_validator_test.go b/avalanchego/vms/platformvm/txs/add_subnet_validator_test.go index 43a5695c..9ed500e0 100644 --- a/avalanchego/vms/platformvm/txs/add_subnet_validator_test.go +++ b/avalanchego/vms/platformvm/txs/add_subnet_validator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/avax" @@ -18,6 +19,7 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) +// TODO use table tests here func TestAddSubnetValidatorTxSyntacticVerify(t *testing.T) { require := require.New(t) clk := mockable.Clock{} @@ -31,7 +33,7 @@ func TestAddSubnetValidatorTxSyntacticVerify(t *testing.T) { ) // Case : signed tx is nil - require.ErrorIs(stx.SyntacticVerify(ctx), errNilSignedTx) + require.ErrorIs(stx.SyntacticVerify(ctx), ErrNilSignedTx) // Case : unsigned tx is nil require.ErrorIs(addSubnetValidatorTx.SyntacticVerify(ctx), ErrNilTx) @@ -117,11 +119,21 @@ func TestAddSubnetValidatorTxSyntacticVerify(t *testing.T) { // Case: Subnet auth indices not unique addSubnetValidatorTx.SyntacticallyVerified = false input := addSubnetValidatorTx.SubnetAuth.(*secp256k1fx.Input) + oldInput := *input input.SigIndices[0] = input.SigIndices[1] stx, err = NewSigned(addSubnetValidatorTx, Codec, signers) require.NoError(err) err = stx.SyntacticVerify(ctx) require.Error(err) + *input = oldInput + + // Case: adding to Primary Network + addSubnetValidatorTx.SyntacticallyVerified = false + addSubnetValidatorTx.Validator.Subnet = constants.PrimaryNetworkID + stx, err = NewSigned(addSubnetValidatorTx, Codec, signers) + require.NoError(err) + err = stx.SyntacticVerify(ctx) + require.ErrorIs(err, errAddPrimaryNetworkValidator) } func TestAddSubnetValidatorMarshal(t *testing.T) { @@ -197,3 +209,21 @@ func TestAddSubnetValidatorMarshal(t *testing.T) { require.NoError(parsedTx.SyntacticVerify(ctx)) require.Equal(stx, parsedTx) } + +func TestAddSubnetValidatorTxNotValidatorTx(t *testing.T) { + txIntf := any((*AddSubnetValidatorTx)(nil)) + _, ok := txIntf.(ValidatorTx) + require.False(t, ok) +} + +func TestAddSubnetValidatorTxNotDelegatorTx(t *testing.T) { + txIntf := any((*AddSubnetValidatorTx)(nil)) + _, ok := txIntf.(DelegatorTx) + require.False(t, ok) +} + +func TestAddSubnetValidatorTxNotPermissionlessStaker(t *testing.T) { + txIntf := any((*AddSubnetValidatorTx)(nil)) + _, ok := txIntf.(PermissionlessStaker) + require.False(t, ok) +} diff --git a/avalanchego/vms/platformvm/txs/add_subnet_validator_tx.go b/avalanchego/vms/platformvm/txs/add_subnet_validator_tx.go index 246185a9..3cc50d40 100644 --- a/avalanchego/vms/platformvm/txs/add_subnet_validator_tx.go +++ b/avalanchego/vms/platformvm/txs/add_subnet_validator_tx.go @@ -1,21 +1,23 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs import ( + "errors" "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/vms/components/verify" "github.com/ava-labs/avalanchego/vms/platformvm/validator" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) var ( - _ UnsignedTx = &AddSubnetValidatorTx{} - _ StakerTx = &AddSubnetValidatorTx{} - _ secp256k1fx.UnsignedTx = &AddSubnetValidatorTx{} + _ StakerTx = &AddSubnetValidatorTx{} + + errAddPrimaryNetworkValidator = errors.New("can't add primary network validator with AddSubnetValidatorTx") ) // AddSubnetValidatorTx is an unsigned addSubnetValidatorTx @@ -28,19 +30,18 @@ type AddSubnetValidatorTx struct { SubnetAuth verify.Verifiable `serialize:"true" json:"subnetAuthorization"` } -// StartTime of this validator -func (tx *AddSubnetValidatorTx) StartTime() time.Time { - return tx.Validator.StartTime() -} +func (tx *AddSubnetValidatorTx) SubnetID() ids.ID { return tx.Validator.Subnet } +func (tx *AddSubnetValidatorTx) NodeID() ids.NodeID { return tx.Validator.NodeID } +func (tx *AddSubnetValidatorTx) StartTime() time.Time { return tx.Validator.StartTime() } +func (tx *AddSubnetValidatorTx) EndTime() time.Time { return tx.Validator.EndTime() } +func (tx *AddSubnetValidatorTx) Weight() uint64 { return tx.Validator.Wght } -// EndTime of this validator -func (tx *AddSubnetValidatorTx) EndTime() time.Time { - return tx.Validator.EndTime() +func (tx *AddSubnetValidatorTx) PendingPriority() Priority { + return SubnetPermissionedValidatorPendingPriority } -// Weight of this validator -func (tx *AddSubnetValidatorTx) Weight() uint64 { - return tx.Validator.Weight() +func (tx *AddSubnetValidatorTx) CurrentPriority() Priority { + return SubnetPermissionedValidatorCurrentPriority } // SyntacticVerify returns nil iff [tx] is valid @@ -50,6 +51,8 @@ func (tx *AddSubnetValidatorTx) SyntacticVerify(ctx *snow.Context) error { return ErrNilTx case tx.SyntacticallyVerified: // already passed syntactic verification return nil + case tx.Validator.Subnet == constants.PrimaryNetworkID: + return errAddPrimaryNetworkValidator } if err := tx.BaseTx.SyntacticVerify(ctx); err != nil { diff --git a/avalanchego/vms/platformvm/txs/add_validator_test.go b/avalanchego/vms/platformvm/txs/add_validator_test.go index 6b38371d..14f333c1 100644 --- a/avalanchego/vms/platformvm/txs/add_validator_test.go +++ b/avalanchego/vms/platformvm/txs/add_validator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -34,7 +34,7 @@ func TestAddValidatorTxSyntacticVerify(t *testing.T) { ) // Case : signed tx is nil - require.ErrorIs(stx.SyntacticVerify(ctx), errNilSignedTx) + require.ErrorIs(stx.SyntacticVerify(ctx), ErrNilSignedTx) // Case : unsigned tx is nil require.ErrorIs(addValidatorTx.SyntacticVerify(ctx), ErrNilTx) @@ -88,13 +88,13 @@ func TestAddValidatorTxSyntacticVerify(t *testing.T) { End: uint64(clk.Time().Add(time.Hour).Unix()), Wght: validatorWeight, }, - Stake: stakes, + StakeOuts: stakes, RewardsOwner: &secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, Addrs: []ids.ShortID{rewardAddress}, }, - Shares: reward.PercentDenominator, + DelegationShares: reward.PercentDenominator, } // Case: valid tx @@ -113,7 +113,7 @@ func TestAddValidatorTxSyntacticVerify(t *testing.T) { // Case: Stake owner has no addresses addValidatorTx.SyntacticallyVerified = false - addValidatorTx.Stake[0]. + addValidatorTx.StakeOuts[0]. Out.(*stakeable.LockOut). TransferableOut.(*secp256k1fx.TransferOutput). Addrs = nil @@ -121,7 +121,7 @@ func TestAddValidatorTxSyntacticVerify(t *testing.T) { require.NoError(err) err = stx.SyntacticVerify(ctx) require.Error(err) - addValidatorTx.Stake = stakes + addValidatorTx.StakeOuts = stakes // Case: Rewards owner has no addresses addValidatorTx.SyntacticallyVerified = false @@ -134,12 +134,12 @@ func TestAddValidatorTxSyntacticVerify(t *testing.T) { // Case: Too many shares addValidatorTx.SyntacticallyVerified = false - addValidatorTx.Shares++ // 1 more than max amount + addValidatorTx.DelegationShares++ // 1 more than max amount stx, err = NewSigned(addValidatorTx, Codec, signers) require.NoError(err) err = stx.SyntacticVerify(ctx) require.Error(err) - addValidatorTx.Shares-- + addValidatorTx.DelegationShares-- } func TestAddValidatorTxSyntacticVerifyNotAVAX(t *testing.T) { @@ -205,16 +205,22 @@ func TestAddValidatorTxSyntacticVerifyNotAVAX(t *testing.T) { End: uint64(clk.Time().Add(time.Hour).Unix()), Wght: validatorWeight, }, - Stake: stakes, + StakeOuts: stakes, RewardsOwner: &secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, Addrs: []ids.ShortID{rewardAddress}, }, - Shares: reward.PercentDenominator, + DelegationShares: reward.PercentDenominator, } stx, err = NewSigned(addValidatorTx, Codec, signers) require.NoError(err) require.Error(stx.SyntacticVerify(ctx)) } + +func TestAddValidatorTxNotDelegatorTx(t *testing.T) { + txIntf := any((*AddValidatorTx)(nil)) + _, ok := txIntf.(DelegatorTx) + require.False(t, ok) +} diff --git a/avalanchego/vms/platformvm/txs/add_validator_tx.go b/avalanchego/vms/platformvm/txs/add_validator_tx.go index 88db9ff7..aaeb7353 100644 --- a/avalanchego/vms/platformvm/txs/add_validator_tx.go +++ b/avalanchego/vms/platformvm/txs/add_validator_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -7,7 +7,9 @@ import ( "fmt" "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/verify" @@ -18,9 +20,7 @@ import ( ) var ( - _ UnsignedTx = &AddValidatorTx{} - _ StakerTx = &AddValidatorTx{} - _ secp256k1fx.UnsignedTx = &AddValidatorTx{} + _ ValidatorTx = &AddValidatorTx{} errTooManyShares = fmt.Errorf("a staker can only require at most %d shares from delegators", reward.PercentDenominator) ) @@ -32,12 +32,13 @@ type AddValidatorTx struct { // Describes the delegatee Validator validator.Validator `serialize:"true" json:"validator"` // Where to send staked tokens when done validating - Stake []*avax.TransferableOutput `serialize:"true" json:"stake"` + StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"` // Where to send staking rewards when done validating RewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"` // Fee this validator charges delegators as a percentage, times 10,000 - // For example, if this validator has Shares=300,000 then they take 30% of rewards from delegators - Shares uint32 `serialize:"true" json:"shares"` + // For example, if this validator has DelegationShares=300,000 then they + // take 30% of rewards from delegators + DelegationShares uint32 `serialize:"true" json:"shares"` } // InitCtx sets the FxID fields in the inputs and outputs of this @@ -45,27 +46,24 @@ type AddValidatorTx struct { // the addresses can be json marshalled into human readable format func (tx *AddValidatorTx) InitCtx(ctx *snow.Context) { tx.BaseTx.InitCtx(ctx) - for _, out := range tx.Stake { + for _, out := range tx.StakeOuts { out.FxID = secp256k1fx.ID out.InitCtx(ctx) } tx.RewardsOwner.InitCtx(ctx) } -// StartTime of this validator -func (tx *AddValidatorTx) StartTime() time.Time { - return tx.Validator.StartTime() -} - -// EndTime of this validator -func (tx *AddValidatorTx) EndTime() time.Time { - return tx.Validator.EndTime() -} - -// Weight of this validator -func (tx *AddValidatorTx) Weight() uint64 { - return tx.Validator.Weight() -} +func (tx *AddValidatorTx) SubnetID() ids.ID { return constants.PrimaryNetworkID } +func (tx *AddValidatorTx) NodeID() ids.NodeID { return tx.Validator.NodeID } +func (tx *AddValidatorTx) StartTime() time.Time { return tx.Validator.StartTime() } +func (tx *AddValidatorTx) EndTime() time.Time { return tx.Validator.EndTime() } +func (tx *AddValidatorTx) Weight() uint64 { return tx.Validator.Wght } +func (tx *AddValidatorTx) PendingPriority() Priority { return PrimaryNetworkValidatorPendingPriority } +func (tx *AddValidatorTx) CurrentPriority() Priority { return PrimaryNetworkValidatorCurrentPriority } +func (tx *AddValidatorTx) Stake() []*avax.TransferableOutput { return tx.StakeOuts } +func (tx *AddValidatorTx) ValidationRewardsOwner() fx.Owner { return tx.RewardsOwner } +func (tx *AddValidatorTx) DelegationRewardsOwner() fx.Owner { return tx.RewardsOwner } +func (tx *AddValidatorTx) Shares() uint32 { return tx.DelegationShares } // SyntacticVerify returns nil iff [tx] is valid func (tx *AddValidatorTx) SyntacticVerify(ctx *snow.Context) error { @@ -74,7 +72,7 @@ func (tx *AddValidatorTx) SyntacticVerify(ctx *snow.Context) error { return ErrNilTx case tx.SyntacticallyVerified: // already passed syntactic verification return nil - case tx.Shares > reward.PercentDenominator: // Ensure delegators shares are in the allowed amount + case tx.DelegationShares > reward.PercentDenominator: // Ensure delegators shares are in the allowed amount return errTooManyShares } @@ -86,7 +84,7 @@ func (tx *AddValidatorTx) SyntacticVerify(ctx *snow.Context) error { } totalStakeWeight := uint64(0) - for _, out := range tx.Stake { + for _, out := range tx.StakeOuts { if err := out.Verify(); err != nil { return fmt.Errorf("failed to verify output: %w", err) } @@ -103,10 +101,10 @@ func (tx *AddValidatorTx) SyntacticVerify(ctx *snow.Context) error { } switch { - case !avax.IsSortedTransferableOutputs(tx.Stake, Codec): + case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec): return errOutputsNotSorted case totalStakeWeight != tx.Validator.Wght: - return fmt.Errorf("validator weight %d is not equal to total stake weight %d", tx.Validator.Wght, totalStakeWeight) + return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, tx.Validator.Wght, totalStakeWeight) } // cache that this is valid diff --git a/avalanchego/vms/platformvm/txs/advance_time_tx.go b/avalanchego/vms/platformvm/txs/advance_time_tx.go index 00dc611a..67f6ec67 100644 --- a/avalanchego/vms/platformvm/txs/advance_time_tx.go +++ b/avalanchego/vms/platformvm/txs/advance_time_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/platformvm/txs/base_tx.go b/avalanchego/vms/platformvm/txs/base_tx.go index 41498e03..dd027f56 100644 --- a/avalanchego/vms/platformvm/txs/base_tx.go +++ b/avalanchego/vms/platformvm/txs/base_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -26,7 +26,7 @@ type BaseTx struct { avax.BaseTx `serialize:"true"` // true iff this transaction has already passed syntactic verification - SyntacticallyVerified bool + SyntacticallyVerified bool `json:"-"` unsignedBytes []byte // Unsigned byte representation of this data } diff --git a/avalanchego/vms/platformvm/txs/base_tx_test.go b/avalanchego/vms/platformvm/txs/base_tx_test.go index 0740016d..d65c8731 100644 --- a/avalanchego/vms/platformvm/txs/base_tx_test.go +++ b/avalanchego/vms/platformvm/txs/base_tx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/platformvm/txs/builder/builder.go b/avalanchego/vms/platformvm/txs/builder/builder.go index cf8c8654..9e1bbb9c 100644 --- a/avalanchego/vms/platformvm/txs/builder/builder.go +++ b/avalanchego/vms/platformvm/txs/builder/builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package builder @@ -148,6 +148,17 @@ type ProposalTxBuilder interface { changeAddr ids.ShortID, ) (*txs.Tx, error) + // Creates a transaction that removes [nodeID] + // as a validator from [subnetID] + // keys: keys to use for removing the validator + // changeAddr: address to send change to, if there is any + NewRemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + keys []*crypto.PrivateKeySECP256K1R, + changeAddr ids.ShortID, + ) (*txs.Tx, error) + // newAdvanceTimeTx creates a new tx that, if it is accepted and followed by a // Commit block, will set the chain's timestamp to [timestamp]. NewAdvanceTimeTx(timestamp time.Time) (*txs.Tx, error) @@ -159,7 +170,7 @@ type ProposalTxBuilder interface { func New( ctx *snow.Context, - cfg config.Config, + cfg *config.Config, clk *mockable.Clock, fx fx.Fx, state state.Chain, @@ -182,7 +193,7 @@ type builder struct { utxo.Spender state state.Chain - cfg config.Config + cfg *config.Config ctx *snow.Context clk *mockable.Clock fx fx.Fx @@ -424,7 +435,7 @@ func (b *builder) NewAddValidatorTx( keys []*crypto.PrivateKeySECP256K1R, changeAddr ids.ShortID, ) (*txs.Tx, error) { - ins, unstakedOuts, stakedOuts, signers, err := b.Spend(keys, stakeAmount, b.cfg.AddStakerTxFee, changeAddr) + ins, unstakedOuts, stakedOuts, signers, err := b.Spend(keys, stakeAmount, b.cfg.AddPrimaryNetworkValidatorFee, changeAddr) if err != nil { return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err) } @@ -442,13 +453,13 @@ func (b *builder) NewAddValidatorTx( End: endTime, Wght: stakeAmount, }, - Stake: stakedOuts, + StakeOuts: stakedOuts, RewardsOwner: &secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, Addrs: []ids.ShortID{rewardAddress}, }, - Shares: shares, + DelegationShares: shares, } tx, err := txs.NewSigned(utx, txs.Codec, signers) if err != nil { @@ -466,7 +477,7 @@ func (b *builder) NewAddDelegatorTx( keys []*crypto.PrivateKeySECP256K1R, changeAddr ids.ShortID, ) (*txs.Tx, error) { - ins, unlockedOuts, lockedOuts, signers, err := b.Spend(keys, stakeAmount, b.cfg.AddStakerTxFee, changeAddr) + ins, unlockedOuts, lockedOuts, signers, err := b.Spend(keys, stakeAmount, b.cfg.AddPrimaryNetworkDelegatorFee, changeAddr) if err != nil { return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err) } @@ -484,8 +495,8 @@ func (b *builder) NewAddDelegatorTx( End: endTime, Wght: stakeAmount, }, - Stake: lockedOuts, - RewardsOwner: &secp256k1fx.OutputOwners{ + StakeOuts: lockedOuts, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, Addrs: []ids.ShortID{rewardAddress}, @@ -544,6 +555,42 @@ func (b *builder) NewAddSubnetValidatorTx( return tx, tx.SyntacticVerify(b.ctx) } +func (b *builder) NewRemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + keys []*crypto.PrivateKeySECP256K1R, + changeAddr ids.ShortID, +) (*txs.Tx, error) { + ins, outs, _, signers, err := b.Spend(keys, 0, b.cfg.TxFee, changeAddr) + if err != nil { + return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err) + } + + subnetAuth, subnetSigners, err := b.Authorize(b.state, subnetID, keys) + if err != nil { + return nil, fmt.Errorf("couldn't authorize tx's subnet restrictions: %w", err) + } + signers = append(signers, subnetSigners) + + // Create the tx + utx := &txs.RemoveSubnetValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: b.ctx.NetworkID, + BlockchainID: b.ctx.ChainID, + Ins: ins, + Outs: outs, + }}, + Subnet: subnetID, + NodeID: nodeID, + SubnetAuth: subnetAuth, + } + tx, err := txs.NewSigned(utx, txs.Codec, signers) + if err != nil { + return nil, err + } + return tx, tx.SyntacticVerify(b.ctx) +} + func (b *builder) NewAdvanceTimeTx(timestamp time.Time) (*txs.Tx, error) { utx := &txs.AdvanceTimeTx{Time: uint64(timestamp.Unix())} tx, err := txs.NewSigned(utx, txs.Codec, nil) diff --git a/avalanchego/vms/platformvm/txs/builder/mock_builder.go b/avalanchego/vms/platformvm/txs/builder/mock_builder.go new file mode 100644 index 00000000..6e7d8350 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/builder/mock_builder.go @@ -0,0 +1,188 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/vms/platformvm/txs/builder (interfaces: Builder) + +// Package builder is a generated GoMock package. +package builder + +import ( + reflect "reflect" + time "time" + + ids "github.com/ava-labs/avalanchego/ids" + crypto "github.com/ava-labs/avalanchego/utils/crypto" + txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" + gomock "github.com/golang/mock/gomock" +) + +// MockBuilder is a mock of Builder interface. +type MockBuilder struct { + ctrl *gomock.Controller + recorder *MockBuilderMockRecorder +} + +// MockBuilderMockRecorder is the mock recorder for MockBuilder. +type MockBuilderMockRecorder struct { + mock *MockBuilder +} + +// NewMockBuilder creates a new mock instance. +func NewMockBuilder(ctrl *gomock.Controller) *MockBuilder { + mock := &MockBuilder{ctrl: ctrl} + mock.recorder = &MockBuilderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBuilder) EXPECT() *MockBuilderMockRecorder { + return m.recorder +} + +// NewAddDelegatorTx mocks base method. +func (m *MockBuilder) NewAddDelegatorTx(arg0, arg1, arg2 uint64, arg3 ids.NodeID, arg4 ids.ShortID, arg5 []*crypto.PrivateKeySECP256K1R, arg6 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewAddDelegatorTx", arg0, arg1, arg2, arg3, arg4, arg5, arg6) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewAddDelegatorTx indicates an expected call of NewAddDelegatorTx. +func (mr *MockBuilderMockRecorder) NewAddDelegatorTx(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAddDelegatorTx", reflect.TypeOf((*MockBuilder)(nil).NewAddDelegatorTx), arg0, arg1, arg2, arg3, arg4, arg5, arg6) +} + +// NewAddSubnetValidatorTx mocks base method. +func (m *MockBuilder) NewAddSubnetValidatorTx(arg0, arg1, arg2 uint64, arg3 ids.NodeID, arg4 ids.ID, arg5 []*crypto.PrivateKeySECP256K1R, arg6 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewAddSubnetValidatorTx", arg0, arg1, arg2, arg3, arg4, arg5, arg6) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewAddSubnetValidatorTx indicates an expected call of NewAddSubnetValidatorTx. +func (mr *MockBuilderMockRecorder) NewAddSubnetValidatorTx(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAddSubnetValidatorTx", reflect.TypeOf((*MockBuilder)(nil).NewAddSubnetValidatorTx), arg0, arg1, arg2, arg3, arg4, arg5, arg6) +} + +// NewAddValidatorTx mocks base method. +func (m *MockBuilder) NewAddValidatorTx(arg0, arg1, arg2 uint64, arg3 ids.NodeID, arg4 ids.ShortID, arg5 uint32, arg6 []*crypto.PrivateKeySECP256K1R, arg7 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewAddValidatorTx", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewAddValidatorTx indicates an expected call of NewAddValidatorTx. +func (mr *MockBuilderMockRecorder) NewAddValidatorTx(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAddValidatorTx", reflect.TypeOf((*MockBuilder)(nil).NewAddValidatorTx), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) +} + +// NewAdvanceTimeTx mocks base method. +func (m *MockBuilder) NewAdvanceTimeTx(arg0 time.Time) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewAdvanceTimeTx", arg0) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewAdvanceTimeTx indicates an expected call of NewAdvanceTimeTx. +func (mr *MockBuilderMockRecorder) NewAdvanceTimeTx(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAdvanceTimeTx", reflect.TypeOf((*MockBuilder)(nil).NewAdvanceTimeTx), arg0) +} + +// NewCreateChainTx mocks base method. +func (m *MockBuilder) NewCreateChainTx(arg0 ids.ID, arg1 []byte, arg2 ids.ID, arg3 []ids.ID, arg4 string, arg5 []*crypto.PrivateKeySECP256K1R, arg6 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewCreateChainTx", arg0, arg1, arg2, arg3, arg4, arg5, arg6) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewCreateChainTx indicates an expected call of NewCreateChainTx. +func (mr *MockBuilderMockRecorder) NewCreateChainTx(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewCreateChainTx", reflect.TypeOf((*MockBuilder)(nil).NewCreateChainTx), arg0, arg1, arg2, arg3, arg4, arg5, arg6) +} + +// NewCreateSubnetTx mocks base method. +func (m *MockBuilder) NewCreateSubnetTx(arg0 uint32, arg1 []ids.ShortID, arg2 []*crypto.PrivateKeySECP256K1R, arg3 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewCreateSubnetTx", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewCreateSubnetTx indicates an expected call of NewCreateSubnetTx. +func (mr *MockBuilderMockRecorder) NewCreateSubnetTx(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewCreateSubnetTx", reflect.TypeOf((*MockBuilder)(nil).NewCreateSubnetTx), arg0, arg1, arg2, arg3) +} + +// NewExportTx mocks base method. +func (m *MockBuilder) NewExportTx(arg0 uint64, arg1 ids.ID, arg2 ids.ShortID, arg3 []*crypto.PrivateKeySECP256K1R, arg4 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewExportTx", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewExportTx indicates an expected call of NewExportTx. +func (mr *MockBuilderMockRecorder) NewExportTx(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewExportTx", reflect.TypeOf((*MockBuilder)(nil).NewExportTx), arg0, arg1, arg2, arg3, arg4) +} + +// NewImportTx mocks base method. +func (m *MockBuilder) NewImportTx(arg0 ids.ID, arg1 ids.ShortID, arg2 []*crypto.PrivateKeySECP256K1R, arg3 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewImportTx", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewImportTx indicates an expected call of NewImportTx. +func (mr *MockBuilderMockRecorder) NewImportTx(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewImportTx", reflect.TypeOf((*MockBuilder)(nil).NewImportTx), arg0, arg1, arg2, arg3) +} + +// NewRemoveSubnetValidatorTx mocks base method. +func (m *MockBuilder) NewRemoveSubnetValidatorTx(arg0 ids.NodeID, arg1 ids.ID, arg2 []*crypto.PrivateKeySECP256K1R, arg3 ids.ShortID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewRemoveSubnetValidatorTx", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewRemoveSubnetValidatorTx indicates an expected call of NewRemoveSubnetValidatorTx. +func (mr *MockBuilderMockRecorder) NewRemoveSubnetValidatorTx(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewRemoveSubnetValidatorTx", reflect.TypeOf((*MockBuilder)(nil).NewRemoveSubnetValidatorTx), arg0, arg1, arg2, arg3) +} + +// NewRewardValidatorTx mocks base method. +func (m *MockBuilder) NewRewardValidatorTx(arg0 ids.ID) (*txs.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewRewardValidatorTx", arg0) + ret0, _ := ret[0].(*txs.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewRewardValidatorTx indicates an expected call of NewRewardValidatorTx. +func (mr *MockBuilderMockRecorder) NewRewardValidatorTx(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewRewardValidatorTx", reflect.TypeOf((*MockBuilder)(nil).NewRewardValidatorTx), arg0) +} diff --git a/avalanchego/vms/platformvm/txs/codec.go b/avalanchego/vms/platformvm/txs/codec.go index e18b3184..f2ccde3e 100644 --- a/avalanchego/vms/platformvm/txs/codec.go +++ b/avalanchego/vms/platformvm/txs/codec.go @@ -1,12 +1,15 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs import ( + "math" + "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/platformvm/stakeable" "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) @@ -14,22 +17,34 @@ import ( // Version is the current default codec version const Version = 0 -// Codec does serialization and deserialization -var Codec codec.Manager +var ( + Codec codec.Manager + + // GenesisCodec allows txs of larger than usual size to be parsed. + // While this gives flexibility in accommodating large genesis txs + // it must not be used to parse new, unverified txs which instead + // must be processed by Codec + GenesisCodec codec.Manager +) func init() { c := linearcodec.NewDefault() Codec = codec.NewDefaultManager() - - // Order in which type are registered affect the byte representation - // generated by marshalling ops. To maintain codec type ordering, - // we skip positions for the blocks. - c.SkipRegistrations(5) + gc := linearcodec.NewCustomMaxLength(math.MaxInt32) + GenesisCodec = codec.NewManager(math.MaxInt32) errs := wrappers.Errs{} + for _, c := range []linearcodec.Codec{c, gc} { + // Order in which type are registered affect the byte representation + // generated by marshalling ops. To maintain codec type ordering, + // we skip positions for the blocks. + c.SkipRegistrations(5) + + errs.Add(RegisterUnsignedTxsTypes(c)) + } errs.Add( - RegisterUnsignedTxsTypes(c), Codec.RegisterCodec(Version, c), + GenesisCodec.RegisterCodec(Version, gc), ) if errs.Errored() { panic(errs.Err) @@ -67,6 +82,15 @@ func RegisterUnsignedTxsTypes(targetCodec codec.Registry) error { targetCodec.RegisterType(&stakeable.LockIn{}), targetCodec.RegisterType(&stakeable.LockOut{}), + + // Banff additions: + targetCodec.RegisterType(&RemoveSubnetValidatorTx{}), + targetCodec.RegisterType(&TransformSubnetTx{}), + targetCodec.RegisterType(&AddPermissionlessValidatorTx{}), + targetCodec.RegisterType(&AddPermissionlessDelegatorTx{}), + + targetCodec.RegisterType(&signer.Empty{}), + targetCodec.RegisterType(&signer.ProofOfPossession{}), ) return errs.Err } diff --git a/avalanchego/vms/platformvm/txs/create_chain_test.go b/avalanchego/vms/platformvm/txs/create_chain_test.go index ac5eaf5c..cd9eb3cc 100644 --- a/avalanchego/vms/platformvm/txs/create_chain_test.go +++ b/avalanchego/vms/platformvm/txs/create_chain_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs diff --git a/avalanchego/vms/platformvm/txs/create_chain_tx.go b/avalanchego/vms/platformvm/txs/create_chain_tx.go index a858425f..75679513 100644 --- a/avalanchego/vms/platformvm/txs/create_chain_tx.go +++ b/avalanchego/vms/platformvm/txs/create_chain_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -12,7 +12,6 @@ import ( "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/components/verify" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) const ( @@ -21,8 +20,7 @@ const ( ) var ( - _ UnsignedTx = &CreateChainTx{} - _ secp256k1fx.UnsignedTx = &CreateChainTx{} + _ UnsignedTx = &CreateChainTx{} ErrCantValidatePrimaryNetwork = errors.New("new blockchain can't be validated by primary network") diff --git a/avalanchego/vms/platformvm/txs/create_subnet_tx.go b/avalanchego/vms/platformvm/txs/create_subnet_tx.go index 631bdb0e..a119000c 100644 --- a/avalanchego/vms/platformvm/txs/create_subnet_tx.go +++ b/avalanchego/vms/platformvm/txs/create_subnet_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -6,13 +6,9 @@ package txs import ( "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/platformvm/fx" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) -var ( - _ UnsignedTx = &CreateSubnetTx{} - _ secp256k1fx.UnsignedTx = &CreateSubnetTx{} -) +var _ UnsignedTx = &CreateSubnetTx{} // CreateSubnetTx is an unsigned proposal to create a new subnet type CreateSubnetTx struct { diff --git a/avalanchego/vms/platformvm/txs/executor/advance_time_test.go b/avalanchego/vms/platformvm/txs/executor/advance_time_test.go index 3dfc723a..a284757f 100644 --- a/avalanchego/vms/platformvm/txs/executor/advance_time_test.go +++ b/avalanchego/vms/platformvm/txs/executor/advance_time_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -12,7 +12,6 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/vms/platformvm/reward" @@ -21,9 +20,66 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) +// Ensure semantic verification updates the current and pending staker set +// for the primary network +func TestAdvanceTimeTxUpdatePrimaryNetworkStakers(t *testing.T) { + require := require.New(t) + env := newEnvironment() + env.ctx.Lock.Lock() + defer func() { + require.NoError(shutdownEnvironment(env)) + }() + dummyHeight := uint64(1) + + // Case: Timestamp is after next validator start time + // Add a pending validator + pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) + pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) + nodeID := ids.GenerateTestNodeID() + addPendingValidatorTx, err := addPendingValidator(env, pendingValidatorStartTime, pendingValidatorEndTime, nodeID, []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}) + require.NoError(err) + + tx, err := env.txBuilder.NewAdvanceTimeTx(pendingValidatorStartTime) + require.NoError(err) + + onCommitState, err := state.NewDiff(lastAcceptedID, env) + require.NoError(err) + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + require.NoError(err) + + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, + Backend: &env.backend, + Tx: tx, + } + require.NoError(tx.Unsigned.Visit(&executor)) + + validatorStaker, err := executor.OnCommitState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + require.Equal(addPendingValidatorTx.ID(), validatorStaker.TxID) + require.EqualValues(0, validatorStaker.PotentialReward) // Rewards are 0 in Flare + + _, err = executor.OnCommitState.GetPendingValidator(constants.PrimaryNetworkID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + _, err = executor.OnAbortState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + validatorStaker, err = executor.OnAbortState.GetPendingValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + require.Equal(addPendingValidatorTx.ID(), validatorStaker.TxID) + + // Test VM validators + executor.OnCommitState.Apply(env.state) + env.state.SetHeight(dummyHeight) + require.NoError(env.state.Commit()) + require.True(env.config.Validators.Contains(constants.PrimaryNetworkID, nodeID)) +} + // Ensure semantic verification fails when proposed timestamp is at or before current timestamp func TestAdvanceTimeTxTimestampTooEarly(t *testing.T) { - validators.InitializeDefaultValidators(0) env := newEnvironment() defer func() { if err := shutdownEnvironment(env); err != nil { @@ -36,10 +92,20 @@ func TestAdvanceTimeTxTimestampTooEarly(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -67,10 +133,20 @@ func TestAdvanceTimeTxTimestampTooLate(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -102,10 +178,20 @@ func TestAdvanceTimeTxTimestampTooLate(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -115,58 +201,6 @@ func TestAdvanceTimeTxTimestampTooLate(t *testing.T) { } } -// Ensure semantic verification updates the current and pending staker set -// for the primary network -func TestAdvanceTimeTxUpdatePrimaryNetworkStakers(t *testing.T) { - require := require.New(t) - env := newEnvironment() - env.ctx.Lock.Lock() - defer func() { - require.NoError(shutdownEnvironment(env)) - }() - dummyHeight := uint64(1) - - // Case: Timestamp is after next validator start time - // Add a pending validator - pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) - pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) - nodeID := ids.GenerateTestNodeID() - addPendingValidatorTx, err := addPendingValidator(env, pendingValidatorStartTime, pendingValidatorEndTime, nodeID, []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}) - require.NoError(err) - - tx, err := env.txBuilder.NewAdvanceTimeTx(pendingValidatorStartTime) - require.NoError(err) - - executor := ProposalTxExecutor{ - Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, - Tx: tx, - } - require.NoError(tx.Unsigned.Visit(&executor)) - - validatorStaker, err := executor.OnCommit.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) - require.NoError(err) - require.Equal(addPendingValidatorTx.ID(), validatorStaker.TxID) - require.EqualValues(0, validatorStaker.PotentialReward) - - _, err = executor.OnCommit.GetPendingValidator(constants.PrimaryNetworkID, nodeID) - require.ErrorIs(err, database.ErrNotFound) - - _, err = executor.OnAbort.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) - require.ErrorIs(err, database.ErrNotFound) - - validatorStaker, err = executor.OnAbort.GetPendingValidator(constants.PrimaryNetworkID, nodeID) - require.NoError(err) - require.Equal(addPendingValidatorTx.ID(), validatorStaker.TxID) - - // Test VM validators - executor.OnCommit.Apply(env.state) - env.state.SetHeight(dummyHeight) - require.NoError(env.state.Commit()) - require.True(env.config.Validators.Contains(constants.PrimaryNetworkID, nodeID)) -} - // Ensure semantic verification updates the current and pending staker sets correctly. // Namely, it should add pending stakers whose start time is at or before the timestamp. // It will not remove primary network stakers; that happens in rewardTxs. @@ -369,9 +403,10 @@ func TestAdvanceTimeTxUpdateStakers(t *testing.T) { ) require.NoError(err) - staker := state.NewSubnetStaker(tx.ID(), &tx.Unsigned.(*txs.AddSubnetValidatorTx).Validator) - staker.NextTime = staker.StartTime - staker.Priority = state.SubnetValidatorPendingPriority + staker := state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) env.state.PutPendingValidator(staker) env.state.AddTx(tx, status.Committed) @@ -384,15 +419,21 @@ func TestAdvanceTimeTxUpdateStakers(t *testing.T) { tx, err := env.txBuilder.NewAdvanceTimeTx(newTime) require.NoError(err) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + require.NoError(err) + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + require.NoError(err) + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.NoError(tx.Unsigned.Visit(&executor)) - executor.OnCommit.Apply(env.state) + executor.OnCommitState.Apply(env.state) } env.state.SetHeight(dummyHeight) require.NoError(env.state.Commit()) @@ -451,9 +492,11 @@ func TestAdvanceTimeTxRemoveSubnetValidator(t *testing.T) { ) require.NoError(err) - staker := state.NewSubnetStaker(tx.ID(), &tx.Unsigned.(*txs.AddSubnetValidatorTx).Validator) - staker.NextTime = staker.EndTime - staker.Priority = state.SubnetValidatorCurrentPriority + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + 0, + ) env.state.PutCurrentValidator(staker) env.state.AddTx(tx, status.Committed) @@ -475,9 +518,10 @@ func TestAdvanceTimeTxRemoveSubnetValidator(t *testing.T) { ) require.NoError(err) - staker = state.NewSubnetStaker(tx.ID(), &tx.Unsigned.(*txs.AddSubnetValidatorTx).Validator) - staker.NextTime = staker.StartTime - staker.Priority = state.SubnetValidatorPendingPriority + staker = state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) env.state.PutPendingValidator(staker) env.state.AddTx(tx, status.Committed) @@ -491,19 +535,25 @@ func TestAdvanceTimeTxRemoveSubnetValidator(t *testing.T) { tx, err = env.txBuilder.NewAdvanceTimeTx(subnetVdr1EndTime) require.NoError(err) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + require.NoError(err) + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + require.NoError(err) + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.NoError(tx.Unsigned.Visit(&executor)) - _, err = executor.OnCommit.GetCurrentValidator(testSubnet1.ID(), subnetValidatorNodeID) + _, err = executor.OnCommitState.GetCurrentValidator(testSubnet1.ID(), subnetValidatorNodeID) require.ErrorIs(err, database.ErrNotFound) // Check VM Validators are removed successfully - executor.OnCommit.Apply(env.state) + executor.OnCommitState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(env.state.Commit()) require.False(env.config.Validators.Contains(testSubnet1.ID(), subnetVdr2NodeID)) @@ -542,9 +592,10 @@ func TestWhitelistedSubnet(t *testing.T) { t.Fatal(err) } - staker := state.NewSubnetStaker(tx.ID(), &tx.Unsigned.(*txs.AddSubnetValidatorTx).Validator) - staker.NextTime = staker.StartTime - staker.Priority = state.SubnetValidatorPendingPriority + staker := state.NewPendingStaker( + tx.ID(), + tx.Unsigned.(*txs.AddSubnetValidatorTx), + ) env.state.PutPendingValidator(staker) env.state.AddTx(tx, status.Committed) @@ -560,10 +611,20 @@ func TestWhitelistedSubnet(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -571,7 +632,7 @@ func TestWhitelistedSubnet(t *testing.T) { t.Fatal(err) } - executor.OnCommit.Apply(env.state) + executor.OnCommitState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(t, env.state.Commit()) require.Equal(t, whitelist, env.config.Validators.Contains(testSubnet1.ID(), ids.NodeID(subnetValidatorNodeID))) @@ -594,22 +655,38 @@ func TestAdvanceTimeTxDelegatorStakerWeight(t *testing.T) { pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMaxStakingDuration) nodeID := ids.GenerateTestNodeID() - _, err := addPendingValidator(env, pendingValidatorStartTime, pendingValidatorEndTime, nodeID, []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}) + _, err := addPendingValidator( + env, + pendingValidatorStartTime, + pendingValidatorEndTime, + nodeID, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ) require.NoError(t, err) tx, err := env.txBuilder.NewAdvanceTimeTx(pendingValidatorStartTime) require.NoError(t, err) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) require.NoError(t, err) - executor.OnCommit.Apply(env.state) + executor.OnCommitState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(t, env.state.Commit()) @@ -629,14 +706,19 @@ func TestAdvanceTimeTxDelegatorStakerWeight(t *testing.T) { uint64(pendingDelegatorEndTime.Unix()), nodeID, preFundedKeys[0].PublicKey().Address(), - []*crypto.PrivateKeySECP256K1R{preFundedKeys[0], preFundedKeys[1], preFundedKeys[4]}, + []*crypto.PrivateKeySECP256K1R{ + preFundedKeys[0], + preFundedKeys[1], + preFundedKeys[4], + }, ids.ShortEmpty, ) require.NoError(t, err) - staker := state.NewPrimaryNetworkStaker(addDelegatorTx.ID(), &addDelegatorTx.Unsigned.(*txs.AddDelegatorTx).Validator) - staker.NextTime = staker.StartTime - staker.Priority = state.PrimaryNetworkDelegatorPendingPriority + staker := state.NewPendingStaker( + addDelegatorTx.ID(), + addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), + ) env.state.PutPendingDelegator(staker) env.state.AddTx(addDelegatorTx, status.Committed) @@ -647,16 +729,26 @@ func TestAdvanceTimeTxDelegatorStakerWeight(t *testing.T) { tx, err = env.txBuilder.NewAdvanceTimeTx(pendingDelegatorStartTime) require.NoError(t, err) + onCommitState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor = ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) require.NoError(t, err) - executor.OnCommit.Apply(env.state) + executor.OnCommitState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(t, env.state.Commit()) @@ -686,16 +778,26 @@ func TestAdvanceTimeTxDelegatorStakers(t *testing.T) { tx, err := env.txBuilder.NewAdvanceTimeTx(pendingValidatorStartTime) require.NoError(t, err) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) require.NoError(t, err) - executor.OnCommit.Apply(env.state) + executor.OnCommitState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(t, env.state.Commit()) @@ -719,9 +821,10 @@ func TestAdvanceTimeTxDelegatorStakers(t *testing.T) { ) require.NoError(t, err) - staker := state.NewPrimaryNetworkStaker(addDelegatorTx.ID(), &addDelegatorTx.Unsigned.(*txs.AddDelegatorTx).Validator) - staker.NextTime = staker.StartTime - staker.Priority = state.PrimaryNetworkDelegatorPendingPriority + staker := state.NewPendingStaker( + addDelegatorTx.ID(), + addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), + ) env.state.PutPendingDelegator(staker) env.state.AddTx(addDelegatorTx, status.Committed) @@ -732,16 +835,26 @@ func TestAdvanceTimeTxDelegatorStakers(t *testing.T) { tx, err = env.txBuilder.NewAdvanceTimeTx(pendingDelegatorStartTime) require.NoError(t, err) + onCommitState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor = ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) require.NoError(t, err) - executor.OnCommit.Apply(env.state) + executor.OnCommitState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(t, env.state.Commit()) @@ -767,10 +880,20 @@ func TestAdvanceTimeTxInitiallyPrefersCommit(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -781,6 +904,43 @@ func TestAdvanceTimeTxInitiallyPrefersCommit(t *testing.T) { } } +func TestAdvanceTimeTxAfterBanff(t *testing.T) { + env := newEnvironment() + env.ctx.Lock.Lock() + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + env.clk.Set(defaultGenesisTime) // VM's clock reads the genesis time + env.config.BanffTime = defaultGenesisTime.Add(SyncBound) + + // Proposed advancing timestamp to the banff timestamp + tx, err := env.txBuilder.NewAdvanceTimeTx(defaultGenesisTime.Add(SyncBound)) + if err != nil { + t.Fatal(err) + } + + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, + Backend: &env.backend, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + require.ErrorIs(t, err, errAdvanceTimeTxIssuedAfterBanff) +} + // Ensure marshaling/unmarshaling works func TestAdvanceTimeTxUnmarshal(t *testing.T) { env := newEnvironment() @@ -830,12 +990,10 @@ func addPendingValidator( return nil, err } - staker := state.NewPrimaryNetworkStaker( + staker := state.NewPendingStaker( addPendingValidatorTx.ID(), - &addPendingValidatorTx.Unsigned.(*txs.AddValidatorTx).Validator, + addPendingValidatorTx.Unsigned.(*txs.AddValidatorTx), ) - staker.NextTime = staker.StartTime - staker.Priority = state.PrimaryNetworkValidatorPendingPriority env.state.PutPendingValidator(staker) env.state.AddTx(addPendingValidatorTx, status.Committed) diff --git a/avalanchego/vms/platformvm/txs/executor/atomic_tx_executor.go b/avalanchego/vms/platformvm/txs/executor/atomic_tx_executor.go index 15868ae2..ade0dc51 100644 --- a/avalanchego/vms/platformvm/txs/executor/atomic_tx_executor.go +++ b/avalanchego/vms/platformvm/txs/executor/atomic_tx_executor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -34,6 +34,18 @@ func (*AtomicTxExecutor) CreateChainTx(*txs.CreateChainTx) error { func (*AtomicTxExecutor) CreateSubnetTx(*txs.CreateSubnetTx) error { return errWrongTxType } func (*AtomicTxExecutor) AdvanceTimeTx(*txs.AdvanceTimeTx) error { return errWrongTxType } func (*AtomicTxExecutor) RewardValidatorTx(*txs.RewardValidatorTx) error { return errWrongTxType } +func (*AtomicTxExecutor) RemoveSubnetValidatorTx(*txs.RemoveSubnetValidatorTx) error { + return errWrongTxType +} +func (*AtomicTxExecutor) TransformSubnetTx(*txs.TransformSubnetTx) error { return errWrongTxType } + +func (*AtomicTxExecutor) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error { + return errWrongTxType +} + +func (*AtomicTxExecutor) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegatorTx) error { + return errWrongTxType +} func (e *AtomicTxExecutor) ImportTx(tx *txs.ImportTx) error { return e.atomicTx(tx) diff --git a/avalanchego/vms/platformvm/txs/executor/backend.go b/avalanchego/vms/platformvm/txs/executor/backend.go index 905f60ba..4f7ac74c 100644 --- a/avalanchego/vms/platformvm/txs/executor/backend.go +++ b/avalanchego/vms/platformvm/txs/executor/backend.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor diff --git a/avalanchego/vms/platformvm/txs/executor/create_chain_test.go b/avalanchego/vms/platformvm/txs/executor/create_chain_test.go index 14451357..551c997f 100644 --- a/avalanchego/vms/platformvm/txs/executor/create_chain_test.go +++ b/avalanchego/vms/platformvm/txs/executor/create_chain_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor diff --git a/avalanchego/vms/platformvm/txs/executor/create_subnet_test.go b/avalanchego/vms/platformvm/txs/executor/create_subnet_test.go index fa7abaa1..c6300c8d 100644 --- a/avalanchego/vms/platformvm/txs/executor/create_subnet_test.go +++ b/avalanchego/vms/platformvm/txs/executor/create_subnet_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor diff --git a/avalanchego/vms/platformvm/txs/executor/export_test.go b/avalanchego/vms/platformvm/txs/executor/export_test.go index abe450af..4f6a93b3 100644 --- a/avalanchego/vms/platformvm/txs/executor/export_test.go +++ b/avalanchego/vms/platformvm/txs/executor/export_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor diff --git a/avalanchego/vms/platformvm/txs/executor/helpers_test.go b/avalanchego/vms/platformvm/txs/executor/helpers_test.go index 13082c0a..36629162 100644 --- a/avalanchego/vms/platformvm/txs/executor/helpers_test.go +++ b/avalanchego/vms/platformvm/txs/executor/helpers_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -143,7 +143,7 @@ func newEnvironment() *environment { txBuilder := builder.New( ctx, - config, + &config, &clk, fx, baseState, @@ -253,7 +253,7 @@ func defaultState( if err := state.Commit(); err != nil { panic(err) } - + lastAcceptedID = state.GetLastAccepted() return state } @@ -303,6 +303,7 @@ func defaultConfig() config.Config { }, ApricotPhase3Time: defaultValidateEndTime, ApricotPhase5Time: defaultValidateEndTime, + BanffTime: mockable.MaxTime, } } @@ -355,14 +356,14 @@ func buildGenesisTest(ctx *snow.Context) []byte { } } - genesisValidators := make([]api.PrimaryValidator, len(preFundedKeys)) + genesisValidators := make([]api.PermissionlessValidator, len(preFundedKeys)) for i, key := range preFundedKeys { nodeID := ids.NodeID(key.PublicKey().Address()) addr, err := address.FormatBech32(hrp, nodeID.Bytes()) if err != nil { panic(err) } - genesisValidators[i] = api.PrimaryValidator{ + genesisValidators[i] = api.PermissionlessValidator{ Staker: api.Staker{ StartTime: json.Uint64(defaultValidateStartTime.Unix()), EndTime: json.Uint64(defaultValidateEndTime.Unix()), diff --git a/avalanchego/vms/platformvm/txs/executor/import_test.go b/avalanchego/vms/platformvm/txs/executor/import_test.go index c063a907..bc5c3738 100644 --- a/avalanchego/vms/platformvm/txs/executor/import_test.go +++ b/avalanchego/vms/platformvm/txs/executor/import_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -135,7 +135,7 @@ func TestNewImportTx(t *testing.T) { shouldVerify: true, }, { - description: "attempting to import non-avax from X-chain pre-blueberry", + description: "attempting to import non-avax from X-chain pre-banff", sourceChainID: env.ctx.XChainID, sharedMemory: fundedSharedMemory( env.ctx.XChainID, @@ -145,12 +145,12 @@ func TestNewImportTx(t *testing.T) { }, ), sourceKeys: []*crypto.PrivateKeySECP256K1R{sourceKey}, - timestamp: env.config.BlueberryTime.Add(-time.Second), + timestamp: env.config.BanffTime.Add(-time.Second), shouldErr: false, shouldVerify: false, }, { - description: "attempting to import non-avax from X-chain post-blueberry", + description: "attempting to import non-avax from X-chain post-banff", sourceChainID: env.ctx.XChainID, sharedMemory: fundedSharedMemory( env.ctx.XChainID, @@ -160,7 +160,7 @@ func TestNewImportTx(t *testing.T) { }, ), sourceKeys: []*crypto.PrivateKeySECP256K1R{sourceKey}, - timestamp: env.config.BlueberryTime, + timestamp: env.config.BanffTime, shouldErr: false, shouldVerify: true, }, diff --git a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go index f3ef03cf..d235158e 100644 --- a/avalanchego/vms/platformvm/txs/executor/inflation_settings.go +++ b/avalanchego/vms/platformvm/txs/executor/inflation_settings.go @@ -26,7 +26,7 @@ type InflationSettings struct { MinStakeDuration time.Duration MinDelegateDuration time.Duration MaxStakeDuration time.Duration - MinFutureStartTimeOffset time.Duration + MinFutureStartTimeOffset time.Duration // Will not be checked when addPermissionlessValidator tx is used MaxValidatorWeightFactor uint64 MinStakeStartTime time.Time } @@ -37,6 +37,31 @@ func GetCurrentInflationSettings(currentTimestamp time.Time, networkID uint32, c return s.MinValidatorStake, s.MaxValidatorStake, s.MinDelegatorStake, s.MinDelegationFee, s.MinStakeDuration, s.MinDelegateDuration, s.MaxStakeDuration, s.MinFutureStartTimeOffset, s.MaxValidatorWeightFactor, s.MinStakeStartTime } +func getCurrentValidatorRules(currentTimestamp time.Time, backend *Backend) *addValidatorRules { + s := inflationSettingsVariants.GetValue(backend.Ctx.NetworkID)(currentTimestamp, backend.Config) + return &addValidatorRules{ + assetID: backend.Ctx.AVAXAssetID, + minValidatorStake: s.MinValidatorStake, + maxValidatorStake: s.MaxValidatorStake, + minStakeDuration: s.MinStakeDuration, + maxStakeDuration: s.MaxStakeDuration, + minDelegationFee: s.MinDelegationFee, + minStakeStartTime: s.MinStakeStartTime, + } +} + +func getCurrentDelegatorRules(currentTimestamp time.Time, backend *Backend) *addDelegatorRules { + s := inflationSettingsVariants.GetValue(backend.Ctx.NetworkID)(currentTimestamp, backend.Config) + return &addDelegatorRules{ + assetID: backend.Ctx.AVAXAssetID, + minDelegatorStake: s.MinDelegatorStake, + maxValidatorStake: s.MaxValidatorStake, + minStakeDuration: s.MinDelegateDuration, + maxStakeDuration: s.MaxStakeDuration, + maxValidatorWeightFactor: byte(s.MaxValidatorWeightFactor), + } +} + func getFlareInflationSettings(currentTimestamp time.Time, _ *config.Config) InflationSettings { switch { case currentTimestamp.Before(time.Date(2023, time.October, 1, 0, 0, 0, 0, time.UTC)): @@ -113,9 +138,9 @@ func getLocalFlareInflationSettings(currentTimestamp time.Time, _ *config.Config MinDelegatorStake: 10 * units.KiloAvax, MinDelegationFee: 0, MinStakeDuration: 2 * 7 * 24 * time.Hour, - MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MinDelegateDuration: 1 * time.Hour, MaxStakeDuration: 365 * 24 * time.Hour, - MinFutureStartTimeOffset: 24 * time.Hour, + MinFutureStartTimeOffset: MaxFutureStartTime, MaxValidatorWeightFactor: MaxValidatorWeightFactor, MinStakeStartTime: time.Date(2023, time.April, 10, 15, 0, 0, 0, time.UTC), } @@ -127,7 +152,7 @@ func getLocalFlareInflationSettings(currentTimestamp time.Time, _ *config.Config MinDelegatorStake: 10 * units.KiloAvax, MinDelegationFee: 0, MinStakeDuration: 2 * 7 * 24 * time.Hour, - MinDelegateDuration: 2 * 7 * 24 * time.Hour, + MinDelegateDuration: 1 * time.Hour, MaxStakeDuration: 365 * 24 * time.Hour, MinFutureStartTimeOffset: MaxFutureStartTime, MaxValidatorWeightFactor: MaxValidatorWeightFactor, @@ -168,7 +193,7 @@ func getSongbirdInflationSettings(currentTimestamp time.Time, config *config.Con MaxStakeDuration: 365 * 24 * time.Hour, MinFutureStartTimeOffset: MaxFutureStartTime, MaxValidatorWeightFactor: 15, - MinStakeStartTime: time.Date(2024, time.October, 1, 0, 0, 0, 0, time.UTC), + MinStakeStartTime: time.Date(2024, time.September, 3, 0, 0, 0, 0, time.UTC), } } } @@ -180,7 +205,7 @@ func getCostonInflationSettings(currentTimestamp time.Time, config *config.Confi default: return InflationSettings{ MinValidatorStake: 100 * units.KiloAvax, - MaxValidatorStake: 500 * units.MegaAvax, + MaxValidatorStake: 50 * units.MegaAvax, MinDelegatorStake: 10 * units.KiloAvax, MinDelegationFee: 0, MinStakeDuration: 24 * time.Hour, @@ -188,7 +213,7 @@ func getCostonInflationSettings(currentTimestamp time.Time, config *config.Confi MaxStakeDuration: 365 * 24 * time.Hour, MinFutureStartTimeOffset: MaxFutureStartTime, MaxValidatorWeightFactor: 15, - MinStakeStartTime: time.Date(2024, time.April, 24, 15, 0, 0, 0, time.UTC), + MinStakeStartTime: time.Date(2024, time.July, 30, 0, 0, 0, 0, time.UTC), } } } diff --git a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go index 47c27ec6..5ec294c4 100644 --- a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go +++ b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -11,9 +11,10 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/utxo" @@ -32,37 +33,33 @@ const ( var ( _ txs.Visitor = &ProposalTxExecutor{} - errWeightTooSmall = errors.New("weight of this validator is too low") - errWeightTooLarge = errors.New("weight of this validator is too large") - errStakeTooShort = errors.New("staking period is too short") - errStakeTooLong = errors.New("staking period is too long") - errInsufficientDelegationFee = errors.New("staker charges an insufficient delegation fee") - errFutureStakeTime = fmt.Errorf("staker is attempting to start staking more than %s ahead of the current chain time", MaxFutureStartTime) - errWrongNumberOfCredentials = errors.New("should have the same number of credentials as inputs") - errValidatorSubset = errors.New("all subnets' staking period must be a subset of the primary network") - errStakeOverflow = errors.New("validator stake exceeds limit") - errInvalidState = errors.New("generated output isn't valid state") - errOverDelegated = errors.New("validator would be over delegated") - errShouldBeDSValidator = errors.New("expected validator to be in the primary network") - errWrongTxType = errors.New("wrong transaction type") - errInvalidID = errors.New("invalid ID") - errEmptyNodeID = errors.New("validator nodeID cannot be empty") -) - -var ( - songbirdLatestStakingTime = time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC) + errChildBlockNotAfterParent = errors.New("proposed timestamp not after current chain time") + errInvalidState = errors.New("generated output isn't valid state") + errShouldBePermissionlessStaker = errors.New("expected permissionless staker") + errWrongTxType = errors.New("wrong transaction type") + errInvalidID = errors.New("invalid ID") + errProposedAddStakerTxAfterBanff = errors.New("staker transaction proposed after Banff") + errAdvanceTimeTxIssuedAfterBanff = errors.New("AdvanceTimeTx issued after Banff") ) type ProposalTxExecutor struct { // inputs, to be filled before visitor methods are called *Backend - ParentID ids.ID - StateVersions state.Versions - Tx *txs.Tx - - // outputs of visitor execution - OnCommit state.Diff - OnAbort state.Diff + Tx *txs.Tx + // [OnCommitState] is the state used for validation. + // In practice, both [OnCommitState] and [onAbortState] are + // identical when passed into this struct, so we could use either. + // [OnCommitState] is modified by this struct's methods to + // reflect changes made to the state if the proposal is committed. + OnCommitState state.Diff + // [OnAbortState] is modified by this struct's methods to + // reflect changes made to the state if the proposal is aborted. + OnAbortState state.Diff + + // outputs populated by this struct's methods: + // + // [PrefersCommit] is true iff this node initially prefers to + // commit this block transaction. PrefersCommit bool } @@ -70,441 +67,148 @@ func (*ProposalTxExecutor) CreateChainTx(*txs.CreateChainTx) error { return er func (*ProposalTxExecutor) CreateSubnetTx(*txs.CreateSubnetTx) error { return errWrongTxType } func (*ProposalTxExecutor) ImportTx(*txs.ImportTx) error { return errWrongTxType } func (*ProposalTxExecutor) ExportTx(*txs.ExportTx) error { return errWrongTxType } +func (*ProposalTxExecutor) RemoveSubnetValidatorTx(*txs.RemoveSubnetValidatorTx) error { + return errWrongTxType +} +func (*ProposalTxExecutor) TransformSubnetTx(*txs.TransformSubnetTx) error { return errWrongTxType } -func (e *ProposalTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error { - // Verify the tx is well-formed - if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { - return err - } - - parentState, ok := e.StateVersions.GetState(e.ParentID) - if !ok { - return state.ErrMissingParentState - } - currentTimestamp := parentState.GetTimestamp() - - minValidatorStake, maxValidatorStake, _, minDelegationFee, minStakeDuration, _, maxStakeDuration, minFutureStartTimeOffset, _, minStakeStartTime := GetCurrentInflationSettings(currentTimestamp, e.Backend.Ctx.NetworkID, e.Config) - switch { - case tx.Validator.Wght < minValidatorStake: - // Ensure validator is staking at least the minimum amount - return errWeightTooSmall - - case tx.Validator.Wght > maxValidatorStake: - // Ensure validator isn't staking too much - return errWeightTooLarge - - case tx.Shares < minDelegationFee: - // Ensure the validator fee is at least the minimum amount - return errInsufficientDelegationFee - } - duration := tx.Validator.Duration() - switch { - case duration < minStakeDuration: - // Ensure staking length is not too short - return errStakeTooShort +func (*ProposalTxExecutor) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error { + return errWrongTxType +} - case duration > maxStakeDuration: - // Ensure staking length is not too long - return errStakeTooLong - } +func (*ProposalTxExecutor) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegatorTx) error { + return errWrongTxType +} - // Blueberry disallows creating a validator with the empty ID. - if !currentTimestamp.Before(e.Config.BlueberryTime) { - if tx.Validator.NodeID == ids.EmptyNodeID { - return errEmptyNodeID - } +func (e *ProposalTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error { + // AddValidatorTx is a proposal transaction until the Banff fork + // activation. Following the activation, AddValidatorTxs must be issued into + // StandardBlocks. + currentTimestamp := e.OnCommitState.GetTimestamp() + if e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) >= Banff fork time (%s)", + errProposedAddStakerTxAfterBanff, + currentTimestamp, + e.Config.BanffTime, + ) } - outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.Stake)) - copy(outs, tx.Outs) - copy(outs[len(tx.Outs):], tx.Stake) - - if e.Bootstrapped.GetValue() { - // Ensure the proposed validator starts after the current time - startTime := tx.StartTime() - if !currentTimestamp.Before(startTime) { - return fmt.Errorf( - "validator's start time (%s) at or before current timestamp (%s)", - startTime, - currentTimestamp, - ) - } - if !minStakeStartTime.Before(startTime) { - return fmt.Errorf( - "validator's start time (%s) at or before minStakeStartTime (%s)", - startTime, - minStakeStartTime, - ) - } - - _, err := GetValidator(parentState, constants.PrimaryNetworkID, tx.Validator.NodeID) - if err == nil { - return fmt.Errorf( - "attempted to issue duplicate validation for %s", - tx.Validator.NodeID, - ) - } - if err != database.ErrNotFound { - return fmt.Errorf( - "failed to find whether %s is a primary network validator: %w", - tx.Validator.NodeID, - err, - ) - } - - // Verify the flowcheck - if err := e.FlowChecker.VerifySpend( - tx, - parentState, - tx.Ins, - outs, - e.Tx.Creds, - map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: e.Config.AddStakerTxFee, - }, - ); err != nil { - return fmt.Errorf("failed verifySpend: %w", err) - } - - // Make sure the tx doesn't start too far in the future. This is done - // last to allow the verifier visitor to explicitly check for this - // error. - maxStartTime := currentTimestamp.Add(MaxFutureStartTime) - if startTime.After(maxStartTime) { - return errFutureStakeTime - } - minStartTime := maxStartTime.Add(-minFutureStartTimeOffset) - if startTime.Before(minStartTime) { - return fmt.Errorf( - "validator's start time (%s) at or before minStartTime (%s)", - startTime, - minStartTime, - ) - } + onAbortOuts, err := verifyAddValidatorTx( + e.Backend, + e.OnCommitState, + e.Tx, + tx, + ) + if err != nil { + return err } txID := e.Tx.ID() // Set up the state if this tx is committed - onCommit, err := state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } - e.OnCommit = onCommit + // Consume the UTXOs + utxo.Consume(e.OnCommitState, tx.Ins) + // Produce the UTXOs + utxo.Produce(e.OnCommitState, txID, tx.Outs) - // Consume the UTXOS - utxo.Consume(e.OnCommit, tx.Ins) - // Produce the UTXOS - utxo.Produce(e.OnCommit, txID, tx.Outs) - - newStaker := state.NewPrimaryNetworkStaker(txID, &tx.Validator) - newStaker.NextTime = newStaker.StartTime - newStaker.Priority = state.PrimaryNetworkValidatorPendingPriority - e.OnCommit.PutPendingValidator(newStaker) + newStaker := state.NewPendingStaker(txID, tx) + e.OnCommitState.PutPendingValidator(newStaker) // Set up the state if this tx is aborted - onAbort, err := state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } - e.OnAbort = onAbort - - // Consume the UTXOS - utxo.Consume(e.OnAbort, tx.Ins) - // Produce the UTXOS - utxo.Produce(e.OnAbort, txID, outs) + // Consume the UTXOs + utxo.Consume(e.OnAbortState, tx.Ins) + // Produce the UTXOs + utxo.Produce(e.OnAbortState, txID, onAbortOuts) e.PrefersCommit = tx.StartTime().After(e.Clk.Time()) return nil } func (e *ProposalTxExecutor) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error { - // Verify the tx is well-formed - if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { - return err - } - - duration := tx.Validator.Duration() - switch { - case duration < e.Config.MinStakeDuration: - // Ensure staking length is not too short - return errStakeTooShort - - case duration > e.Config.MaxStakeDuration: - // Ensure staking length is not too long - return errStakeTooLong - - case len(e.Tx.Creds) == 0: - // Ensure there is at least one credential for the subnet authorization - return errWrongNumberOfCredentials - } - - if e.Backend.Ctx.NetworkID == constants.FlareID || e.Backend.Ctx.NetworkID == constants.CostwoID || e.Backend.Ctx.NetworkID == constants.StagingID || e.Backend.Ctx.NetworkID == constants.LocalFlareID { - return errStakeTooLong - } - - parentState, ok := e.StateVersions.GetState(e.ParentID) - if !ok { - return state.ErrMissingParentState + // AddSubnetValidatorTx is a proposal transaction until the Banff fork + // activation. Following the activation, AddSubnetValidatorTxs must be + // issued into StandardBlocks. + currentTimestamp := e.OnCommitState.GetTimestamp() + if e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) >= Banff fork time (%s)", + errProposedAddStakerTxAfterBanff, + currentTimestamp, + e.Config.BanffTime, + ) } - if e.Bootstrapped.GetValue() { - currentTimestamp := parentState.GetTimestamp() - // Ensure the proposed validator starts after the current timestamp - validatorStartTime := tx.StartTime() - if !currentTimestamp.Before(validatorStartTime) { - return fmt.Errorf( - "validator's start time (%s) is at or after current chain timestamp (%s)", - currentTimestamp, - validatorStartTime, - ) - } - - _, err := GetValidator(parentState, tx.Validator.Subnet, tx.Validator.NodeID) - if err == nil { - return fmt.Errorf( - "attempted to issue duplicate subnet validation for %s", - tx.Validator.NodeID, - ) - } - if err != database.ErrNotFound { - return fmt.Errorf( - "failed to find whether %s is a subnet validator: %w", - tx.Validator.NodeID, - err, - ) - } - - primaryNetworkValidator, err := GetValidator(parentState, constants.PrimaryNetworkID, tx.Validator.NodeID) - if err != nil { - return fmt.Errorf( - "failed to fetch the primary network validator for %s: %w", - tx.Validator.NodeID, - err, - ) - } - - // Ensure that the period this validator validates the specified subnet - // is a subset of the time they validate the primary network. - if !tx.Validator.BoundedBy(primaryNetworkValidator.StartTime, primaryNetworkValidator.EndTime) { - return errValidatorSubset - } - - baseTxCredsLen := len(e.Tx.Creds) - 1 - baseTxCreds := e.Tx.Creds[:baseTxCredsLen] - subnetCred := e.Tx.Creds[baseTxCredsLen] - - subnetIntf, _, err := parentState.GetTx(tx.Validator.Subnet) - if err != nil { - return fmt.Errorf( - "couldn't find subnet %q: %w", - tx.Validator.Subnet, - err, - ) - } - - subnet, ok := subnetIntf.Unsigned.(*txs.CreateSubnetTx) - if !ok { - return fmt.Errorf( - "%s is not a subnet", - tx.Validator.Subnet, - ) - } - - if err := e.Fx.VerifyPermission(tx, tx.SubnetAuth, subnetCred, subnet.Owner); err != nil { - return err - } - - // Verify the flowcheck - if err := e.FlowChecker.VerifySpend( - tx, - parentState, - tx.Ins, - tx.Outs, - baseTxCreds, - map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: e.Config.TxFee, - }, - ); err != nil { - return err - } - - // Make sure the tx doesn't start too far in the future. This is done - // last to allow the verifier visitor to explicitly check for this - // error. - maxStartTime := currentTimestamp.Add(MaxFutureStartTime) - if validatorStartTime.After(maxStartTime) { - return errFutureStakeTime - } + if err := verifyAddSubnetValidatorTx( + e.Backend, + e.OnCommitState, + e.Tx, + tx, + ); err != nil { + return err } txID := e.Tx.ID() // Set up the state if this tx is committed - onCommit, err := state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } - e.OnCommit = onCommit - - // Consume the UTXOS - utxo.Consume(e.OnCommit, tx.Ins) - // Produce the UTXOS - utxo.Produce(e.OnCommit, txID, tx.Outs) + // Consume the UTXOs + utxo.Consume(e.OnCommitState, tx.Ins) + // Produce the UTXOs + utxo.Produce(e.OnCommitState, txID, tx.Outs) - newStaker := state.NewSubnetStaker(txID, &tx.Validator) - newStaker.NextTime = newStaker.StartTime - newStaker.Priority = state.SubnetValidatorPendingPriority - e.OnCommit.PutPendingValidator(newStaker) + newStaker := state.NewPendingStaker(txID, tx) + e.OnCommitState.PutPendingValidator(newStaker) // Set up the state if this tx is aborted - onAbort, err := state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } - e.OnAbort = onAbort - - // Consume the UTXOS - utxo.Consume(e.OnAbort, tx.Ins) - // Produce the UTXOS - utxo.Produce(e.OnAbort, txID, tx.Outs) + // Consume the UTXOs + utxo.Consume(e.OnAbortState, tx.Ins) + // Produce the UTXOs + utxo.Produce(e.OnAbortState, txID, tx.Outs) e.PrefersCommit = tx.StartTime().After(e.Clk.Time()) return nil } func (e *ProposalTxExecutor) AddDelegatorTx(tx *txs.AddDelegatorTx) error { - // Verify the tx is well-formed - if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { - return err - } - - parentState, ok := e.StateVersions.GetState(e.ParentID) - if !ok { - return state.ErrMissingParentState + // AddDelegatorTx is a proposal transaction until the Banff fork + // activation. Following the activation, AddDelegatorTxs must be issued into + // StandardBlocks. + currentTimestamp := e.OnCommitState.GetTimestamp() + if e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) >= Banff fork time (%s)", + errProposedAddStakerTxAfterBanff, + currentTimestamp, + e.Config.BanffTime, + ) } - currentTimestamp := parentState.GetTimestamp() - - _, maxValidatorStake, minDelegatorStake, _, _, minDelegateDuration, maxStakeDuration, minFutureStartTimeOffset, maxValidatorWeightFactor, _ := GetCurrentInflationSettings(currentTimestamp, e.Backend.Ctx.NetworkID, e.Config) - - duration := tx.Validator.Duration() - switch { - case duration < minDelegateDuration: - // Ensure staking length is not too short - return errStakeTooShort - - case duration > maxStakeDuration: - // Ensure staking length is not too long - return errStakeTooLong - case tx.Validator.Wght < minDelegatorStake: - // Ensure validator is staking at least the minimum amount - return errWeightTooSmall + onAbortOuts, err := verifyAddDelegatorTx( + e.Backend, + e.OnCommitState, + e.Tx, + tx, + ) + if err != nil { + return err } - outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.Stake)) - copy(outs, tx.Outs) - copy(outs[len(tx.Outs):], tx.Stake) - txID := e.Tx.ID() - newStaker := state.NewPrimaryNetworkStaker(txID, &tx.Validator) - newStaker.NextTime = newStaker.StartTime - newStaker.Priority = state.PrimaryNetworkDelegatorPendingPriority - - if e.Bootstrapped.GetValue() { - // Ensure the proposed validator starts after the current timestamp - validatorStartTime := tx.StartTime() - if !currentTimestamp.Before(validatorStartTime) { - return fmt.Errorf( - "chain timestamp (%s) not before validator's start time (%s)", - currentTimestamp, - validatorStartTime, - ) - } - - primaryNetworkValidator, err := GetValidator(parentState, constants.PrimaryNetworkID, tx.Validator.NodeID) - if err != nil { - return fmt.Errorf( - "failed to fetch the primary network validator for %s: %w", - tx.Validator.NodeID, - err, - ) - } - - maximumWeight, err := math.Mul64(maxValidatorWeightFactor, primaryNetworkValidator.Weight) - if err != nil { - return errStakeOverflow - } - - if !currentTimestamp.Before(e.Config.ApricotPhase3Time) { - maximumWeight = math.Min64(maximumWeight, maxValidatorStake) - } - - canDelegate, err := canDelegate(parentState, primaryNetworkValidator, maximumWeight, newStaker) - if err != nil { - return err - } - if !canDelegate { - return errOverDelegated - } - - // Verify the flowcheck - if err := e.FlowChecker.VerifySpend( - tx, - parentState, - tx.Ins, - outs, - e.Tx.Creds, - map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: e.Config.AddStakerTxFee, - }, - ); err != nil { - return fmt.Errorf("failed verifySpend: %w", err) - } - - // Make sure the tx doesn't start too far in the future. This is done - // last to allow the verifier visitor to explicitly check for this - // error. - maxStartTime := currentTimestamp.Add(MaxFutureStartTime) - if validatorStartTime.After(maxStartTime) { - return errFutureStakeTime - } - minStartTime := maxStartTime.Add(-minFutureStartTimeOffset) - if validatorStartTime.Before(minStartTime) { - return fmt.Errorf( - "validator's start time (%s) at or before minStartTime (%s)", - validatorStartTime, - minStartTime, - ) - } - } - // Set up the state if this tx is committed - onCommit, err := state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } - e.OnCommit = onCommit - - // Consume the UTXOS - utxo.Consume(e.OnCommit, tx.Ins) - // Produce the UTXOS - utxo.Produce(e.OnCommit, txID, tx.Outs) + // Consume the UTXOs + utxo.Consume(e.OnCommitState, tx.Ins) + // Produce the UTXOs + utxo.Produce(e.OnCommitState, txID, tx.Outs) - e.OnCommit.PutPendingDelegator(newStaker) + newStaker := state.NewPendingStaker(txID, tx) + e.OnCommitState.PutPendingDelegator(newStaker) // Set up the state if this tx is aborted - onAbort, err := state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } - e.OnAbort = onAbort - - // Consume the UTXOS - utxo.Consume(e.OnAbort, tx.Ins) - // Produce the UTXOS - utxo.Produce(e.OnAbort, txID, outs) + // Consume the UTXOs + utxo.Consume(e.OnAbortState, tx.Ins) + // Produce the UTXOs + utxo.Produce(e.OnAbortState, txID, onAbortOuts) e.PrefersCommit = tx.StartTime().After(e.Clk.Time()) return nil @@ -518,173 +222,55 @@ func (e *ProposalTxExecutor) AdvanceTimeTx(tx *txs.AdvanceTimeTx) error { return errWrongNumberOfCredentials } - txTimestamp := tx.Timestamp() - localTimestamp := e.Clk.Time() - localTimestampPlusSync := localTimestamp.Add(SyncBound) - if localTimestampPlusSync.Before(txTimestamp) { + // Validate [newChainTime] + newChainTime := tx.Timestamp() + if e.Config.IsBanffActivated(newChainTime) { return fmt.Errorf( - "proposed time (%s) is too far in the future relative to local time (%s)", - txTimestamp, - localTimestamp, + "%w: proposed timestamp (%s) >= Banff fork time (%s)", + errAdvanceTimeTxIssuedAfterBanff, + newChainTime, + e.Config.BanffTime, ) } - parentState, ok := e.StateVersions.GetState(e.ParentID) - if !ok { - return state.ErrMissingParentState - } - - if chainTimestamp := parentState.GetTimestamp(); !txTimestamp.After(chainTimestamp) { + parentChainTime := e.OnCommitState.GetTimestamp() + if !newChainTime.After(parentChainTime) { return fmt.Errorf( - "proposed timestamp (%s), not after current timestamp (%s)", - txTimestamp, - chainTimestamp, + "%w, proposed timestamp (%s), chain time (%s)", + errChildBlockNotAfterParent, + parentChainTime, + parentChainTime, ) } // Only allow timestamp to move forward as far as the time of next staker // set change time - nextStakerChangeTime, err := GetNextStakerChangeTime(parentState) + nextStakerChangeTime, err := GetNextStakerChangeTime(e.OnCommitState) if err != nil { return err } - if txTimestamp.After(nextStakerChangeTime) { - return fmt.Errorf( - "proposed timestamp (%s) later than next staker change time (%s)", - txTimestamp, - nextStakerChangeTime, - ) - } - - pendingStakerIterator, err := parentState.GetPendingStakerIterator() - if err != nil { + now := e.Clk.Time() + if err := VerifyNewChainTime( + newChainTime, + nextStakerChangeTime, + now, + ); err != nil { return err } - var ( - currentSupply = parentState.GetCurrentSupply() - currentValidatorsToAdd []*state.Staker - pendingValidatorsToRemove []*state.Staker - currentDelegatorsToAdd []*state.Staker - pendingDelegatorsToRemove []*state.Staker - ) - - // Add to the staker set any pending stakers whose start time is at or - // before the new timestamp - for pendingStakerIterator.Next() { - stakerToRemove := pendingStakerIterator.Value() - if stakerToRemove.StartTime.After(txTimestamp) { - break - } - - stakerToAdd := *stakerToRemove - stakerToAdd.NextTime = stakerToRemove.EndTime - stakerToAdd.Priority = state.PendingToCurrentPriorities[stakerToRemove.Priority] - - switch stakerToRemove.Priority { - case state.PrimaryNetworkDelegatorPendingPriority: - potentialReward := e.Rewards.Calculate( - stakerToRemove.EndTime.Sub(stakerToRemove.StartTime), - stakerToRemove.Weight, - currentSupply, - ) - currentSupply, err = math.Add64(currentSupply, potentialReward) - if err != nil { - pendingStakerIterator.Release() - return err - } - - stakerToAdd.PotentialReward = potentialReward - - currentDelegatorsToAdd = append(currentDelegatorsToAdd, &stakerToAdd) - pendingDelegatorsToRemove = append(pendingDelegatorsToRemove, stakerToRemove) - case state.PrimaryNetworkValidatorPendingPriority: - potentialReward := e.Rewards.Calculate( - stakerToRemove.EndTime.Sub(stakerToRemove.StartTime), - stakerToRemove.Weight, - currentSupply, - ) - currentSupply, err = math.Add64(currentSupply, potentialReward) - if err != nil { - pendingStakerIterator.Release() - return err - } - - stakerToAdd.PotentialReward = potentialReward - - currentValidatorsToAdd = append(currentValidatorsToAdd, &stakerToAdd) - pendingValidatorsToRemove = append(pendingValidatorsToRemove, stakerToRemove) - case state.SubnetValidatorPendingPriority: - // We require that the [txTimestamp] <= [nextStakerChangeTime]. - // Additionally, the minimum stake duration is > 0. This means we - // know that the staker we are adding here should never be attempted - // to be removed in the following loop. - - currentValidatorsToAdd = append(currentValidatorsToAdd, &stakerToAdd) - pendingValidatorsToRemove = append(pendingValidatorsToRemove, stakerToRemove) - default: - pendingStakerIterator.Release() - return fmt.Errorf("expected staker priority got %d", stakerToRemove.Priority) - } - } - pendingStakerIterator.Release() - - currentStakerIterator, err := parentState.GetCurrentStakerIterator() - if err != nil { - return err - } - - var currentValidatorsToRemove []*state.Staker - for currentStakerIterator.Next() { - stakerToRemove := currentStakerIterator.Value() - if stakerToRemove.EndTime.After(txTimestamp) { - break - } - - priority := stakerToRemove.Priority - if priority == state.PrimaryNetworkDelegatorCurrentPriority || - priority == state.PrimaryNetworkValidatorCurrentPriority { - // Primary network stakers are removed by the RewardValidatorTx, not - // an AdvanceTimeTx. - break - } - - currentValidatorsToRemove = append(currentValidatorsToRemove, stakerToRemove) - } - currentStakerIterator.Release() - - e.OnCommit, err = state.NewDiff(e.ParentID, e.StateVersions) + changes, err := AdvanceTimeTo(e.Backend, e.OnCommitState, newChainTime) if err != nil { return err } - e.OnCommit.SetTimestamp(txTimestamp) - e.OnCommit.SetCurrentSupply(currentSupply) - - for _, currentValidatorToAdd := range currentValidatorsToAdd { - e.OnCommit.PutCurrentValidator(currentValidatorToAdd) - } - for _, pendingValidatorToRemove := range pendingValidatorsToRemove { - e.OnCommit.DeletePendingValidator(pendingValidatorToRemove) - } - for _, currentDelegatorToAdd := range currentDelegatorsToAdd { - e.OnCommit.PutCurrentDelegator(currentDelegatorToAdd) - } - for _, pendingDelegatorToRemove := range pendingDelegatorsToRemove { - e.OnCommit.DeletePendingDelegator(pendingDelegatorToRemove) - } - for _, currentValidatorToRemove := range currentValidatorsToRemove { - e.OnCommit.DeleteCurrentValidator(currentValidatorToRemove) - } + // Update the state if this tx is committed + e.OnCommitState.SetTimestamp(newChainTime) + changes.Apply(e.OnCommitState) - // State doesn't change if this proposal is aborted - e.OnAbort, err = state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } + e.PrefersCommit = !newChainTime.After(now.Add(SyncBound)) - e.PrefersCommit = !txTimestamp.After(localTimestampPlusSync) + // Note that state doesn't change if this proposal is aborted return nil } @@ -698,12 +284,7 @@ func (e *ProposalTxExecutor) RewardValidatorTx(tx *txs.RewardValidatorTx) error return errWrongNumberOfCredentials } - parentState, ok := e.StateVersions.GetState(e.ParentID) - if !ok { - return state.ErrMissingParentState - } - - currentStakerIterator, err := parentState.GetCurrentStakerIterator() + currentStakerIterator, err := e.OnCommitState.GetCurrentStakerIterator() if err != nil { return err } @@ -722,7 +303,7 @@ func (e *ProposalTxExecutor) RewardValidatorTx(tx *txs.RewardValidatorTx) error } // Verify that the chain's timestamp is the validator's end time - currentChainTime := parentState.GetTimestamp() + currentChainTime := e.OnCommitState.GetTimestamp() if !stakerToRemove.EndTime.Equal(currentChainTime) { return fmt.Errorf( "attempting to remove TxID: %s before their end time %s", @@ -731,110 +312,233 @@ func (e *ProposalTxExecutor) RewardValidatorTx(tx *txs.RewardValidatorTx) error ) } - stakerTx, _, err := parentState.GetTx(stakerToRemove.TxID) - if err != nil { - return fmt.Errorf("failed to get next removed staker tx: %w", err) - } - - e.OnCommit, err = state.NewDiff(e.ParentID, e.StateVersions) - if err != nil { - return err - } - - e.OnAbort, err = state.NewDiff(e.ParentID, e.StateVersions) + primaryNetworkValidator, err := e.OnCommitState.GetCurrentValidator( + constants.PrimaryNetworkID, + stakerToRemove.NodeID, + ) if err != nil { + // This should never error because the staker set is in memory and + // primary network validators are removed last. return err } - // If the reward is aborted, then the current supply should be decreased. - currentSupply := e.OnAbort.GetCurrentSupply() - newSupply, err := math.Sub64(currentSupply, stakerToRemove.PotentialReward) + stakerTx, _, err := e.OnCommitState.GetTx(stakerToRemove.TxID) if err != nil { - return err + return fmt.Errorf("failed to get next removed staker tx: %w", err) } - e.OnAbort.SetCurrentSupply(newSupply) - var ( - nodeID ids.NodeID - startTime time.Time - ) switch uStakerTx := stakerTx.Unsigned.(type) { - case *txs.AddValidatorTx: - e.OnCommit.DeleteCurrentValidator(stakerToRemove) - e.OnAbort.DeleteCurrentValidator(stakerToRemove) + case txs.ValidatorTx: + e.OnCommitState.DeleteCurrentValidator(stakerToRemove) + e.OnAbortState.DeleteCurrentValidator(stakerToRemove) + + stake := uStakerTx.Stake() + outputs := uStakerTx.Outputs() + // Invariant: The staked asset must be equal to the reward asset. + stakeAsset := stake[0].Asset // Refund the stake here - for i, out := range uStakerTx.Stake { + for i, out := range stake { utxo := &avax.UTXO{ UTXOID: avax.UTXOID{ TxID: tx.TxID, - OutputIndex: uint32(len(uStakerTx.Outs) + i), + OutputIndex: uint32(len(outputs) + i), }, - Asset: avax.Asset{ID: e.Ctx.AVAXAssetID}, + Asset: out.Asset, Out: out.Output(), } - e.OnCommit.AddUTXO(utxo) - e.OnAbort.AddUTXO(utxo) + e.OnCommitState.AddUTXO(utxo) + e.OnAbortState.AddUTXO(utxo) + } + + // Provide the reward here + if stakerToRemove.PotentialReward > 0 { + validationRewardsOwner := uStakerTx.ValidationRewardsOwner() + outIntf, err := e.Fx.CreateOutput(stakerToRemove.PotentialReward, validationRewardsOwner) + if err != nil { + return fmt.Errorf("failed to create output: %w", err) + } + out, ok := outIntf.(verify.State) + if !ok { + return errInvalidState + } + + utxo := &avax.UTXO{ + UTXOID: avax.UTXOID{ + TxID: tx.TxID, + OutputIndex: uint32(len(outputs) + len(stake)), + }, + Asset: stakeAsset, + Out: out, + } + + e.OnCommitState.AddUTXO(utxo) + e.OnCommitState.AddRewardUTXO(tx.TxID, utxo) } - // Handle reward preferences - nodeID = uStakerTx.Validator.ID() - startTime = uStakerTx.StartTime() - case *txs.AddDelegatorTx: - e.OnCommit.DeleteCurrentDelegator(stakerToRemove) - e.OnAbort.DeleteCurrentDelegator(stakerToRemove) + // Invariant: A [txs.DelegatorTx] does not also implement the + // [txs.ValidatorTx] interface. + case txs.DelegatorTx: + e.OnCommitState.DeleteCurrentDelegator(stakerToRemove) + e.OnAbortState.DeleteCurrentDelegator(stakerToRemove) + + stake := uStakerTx.Stake() + outputs := uStakerTx.Outputs() + stakeAsset := stake[0].Asset // Refund the stake here - for i, out := range uStakerTx.Stake { + for i, out := range stake { utxo := &avax.UTXO{ UTXOID: avax.UTXOID{ TxID: tx.TxID, - OutputIndex: uint32(len(uStakerTx.Outs) + i), + OutputIndex: uint32(len(outputs) + i), }, - Asset: avax.Asset{ID: e.Ctx.AVAXAssetID}, + Asset: out.Asset, Out: out.Output(), } - e.OnCommit.AddUTXO(utxo) - e.OnAbort.AddUTXO(utxo) + e.OnCommitState.AddUTXO(utxo) + e.OnAbortState.AddUTXO(utxo) } // We're removing a delegator, so we need to fetch the validator they // are delegated to. - vdrStaker, err := parentState.GetCurrentValidator(constants.PrimaryNetworkID, uStakerTx.Validator.NodeID) + vdrStaker, err := e.OnCommitState.GetCurrentValidator( + stakerToRemove.SubnetID, + stakerToRemove.NodeID, + ) if err != nil { return fmt.Errorf( "failed to get whether %s is a validator: %w", - uStakerTx.Validator.NodeID, + stakerToRemove.NodeID, err, ) } - vdrTxIntf, _, err := parentState.GetTx(vdrStaker.TxID) + vdrTxIntf, _, err := e.OnCommitState.GetTx(vdrStaker.TxID) if err != nil { return fmt.Errorf( "failed to get whether %s is a validator: %w", - uStakerTx.Validator.NodeID, + stakerToRemove.NodeID, err, ) } - vdrTx, ok := vdrTxIntf.Unsigned.(*txs.AddValidatorTx) + // Invariant: Delegators must only be able to reference validator + // transactions that implement [txs.ValidatorTx]. All + // validator transactions implement this interface except the + // AddSubnetValidatorTx. + vdrTx, ok := vdrTxIntf.Unsigned.(txs.ValidatorTx) if !ok { return errWrongTxType } - nodeID = uStakerTx.Validator.ID() - startTime = vdrTx.StartTime() + // Calculate split of reward between delegator/delegatee + // The delegator gives stake to the validatee + validatorShares := vdrTx.Shares() + delegatorShares := reward.PercentDenominator - uint64(validatorShares) // parentTx.Shares <= reward.PercentDenominator so no underflow + delegatorReward := delegatorShares * (stakerToRemove.PotentialReward / reward.PercentDenominator) // delegatorShares <= reward.PercentDenominator so no overflow + // Delay rounding as long as possible for small numbers + if optimisticReward, err := math.Mul64(delegatorShares, stakerToRemove.PotentialReward); err == nil { + delegatorReward = optimisticReward / reward.PercentDenominator + } + delegateeReward := stakerToRemove.PotentialReward - delegatorReward // delegatorReward <= reward so no underflow + + offset := 0 + + // Reward the delegator here + if delegatorReward > 0 { + rewardsOwner := uStakerTx.RewardsOwner() + outIntf, err := e.Fx.CreateOutput(delegatorReward, rewardsOwner) + if err != nil { + return fmt.Errorf("failed to create output: %w", err) + } + out, ok := outIntf.(verify.State) + if !ok { + return errInvalidState + } + utxo := &avax.UTXO{ + UTXOID: avax.UTXOID{ + TxID: tx.TxID, + OutputIndex: uint32(len(outputs) + len(stake)), + }, + Asset: stakeAsset, + Out: out, + } + + e.OnCommitState.AddUTXO(utxo) + e.OnCommitState.AddRewardUTXO(tx.TxID, utxo) + + offset++ + } + + // Reward the delegatee here + if delegateeReward > 0 { + delegationRewardsOwner := vdrTx.DelegationRewardsOwner() + outIntf, err := e.Fx.CreateOutput(delegateeReward, delegationRewardsOwner) + if err != nil { + return fmt.Errorf("failed to create output: %w", err) + } + out, ok := outIntf.(verify.State) + if !ok { + return errInvalidState + } + utxo := &avax.UTXO{ + UTXOID: avax.UTXOID{ + TxID: tx.TxID, + OutputIndex: uint32(len(outputs) + len(stake) + offset), + }, + Asset: stakeAsset, + Out: out, + } + + e.OnCommitState.AddUTXO(utxo) + e.OnCommitState.AddRewardUTXO(tx.TxID, utxo) + } default: - return errShouldBeDSValidator + // Invariant: Permissioned stakers are removed by the advancement of + // time and the current chain timestamp is == this staker's + // EndTime. This means only permissionless stakers should be + // left in the staker set. + return errShouldBePermissionlessStaker + } + + // If the reward is aborted, then the current supply should be decreased. + currentSupply, err := e.OnAbortState.GetCurrentSupply(stakerToRemove.SubnetID) + if err != nil { + return err + } + newSupply, err := math.Sub64(currentSupply, stakerToRemove.PotentialReward) + if err != nil { + return err + } + e.OnAbortState.SetCurrentSupply(stakerToRemove.SubnetID, newSupply) + + var expectedUptimePercentage float64 + if stakerToRemove.SubnetID != constants.PrimaryNetworkID { + transformSubnetIntf, err := e.OnCommitState.GetSubnetTransformation(stakerToRemove.SubnetID) + if err != nil { + return err + } + transformSubnet, ok := transformSubnetIntf.Unsigned.(*txs.TransformSubnetTx) + if !ok { + return errIsNotTransformSubnetTx + } + + expectedUptimePercentage = float64(transformSubnet.UptimeRequirement) / reward.PercentDenominator + } else { + expectedUptimePercentage = e.Config.UptimePercentage } - uptime, err := e.Uptimes.CalculateUptimePercentFrom(nodeID, startTime) + // TODO: calculate subnet uptimes + uptime, err := e.Uptimes.CalculateUptimePercentFrom( + primaryNetworkValidator.NodeID, + primaryNetworkValidator.StartTime, + ) if err != nil { return fmt.Errorf("failed to calculate uptime: %w", err) } - e.PrefersCommit = uptime >= e.Config.UptimePercentage + e.PrefersCommit = uptime >= expectedUptimePercentage return nil } @@ -868,10 +572,6 @@ func GetNextStakerChangeTime(state state.Chain) (time.Time, error) { case hasPendingStaker: return pendingStakerIterator.Value().NextTime, nil default: - // SGB-MERGE - if state.GetNetworkID() == constants.SongbirdID || state.GetNetworkID() == constants.CostonID || state.GetNetworkID() == constants.LocalID { - return songbirdLatestStakingTime, nil - } return time.Time{}, database.ErrNotFound } } diff --git a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor_test.go b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor_test.go index 4aeeafc0..5567ffe4 100644 --- a/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor_test.go +++ b/avalanchego/vms/platformvm/txs/executor/proposal_tx_executor_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -9,8 +9,6 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto" "github.com/ava-labs/avalanchego/utils/hashing" @@ -21,18 +19,12 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) -func TestAddDelegatorTxExecute(t *testing.T) { +func TestProposalTxExecuteAddDelegator(t *testing.T) { dummyHeight := uint64(1) rewardAddress := preFundedKeys[0].PublicKey().Address() nodeID := ids.NodeID(rewardAddress) - factory := crypto.FactorySECP256K1R{} - keyIntf, err := factory.NewPrivateKey() - if err != nil { - t.Fatal(err) - } - newValidatorKey := keyIntf.(*crypto.PrivateKeySECP256K1R) - newValidatorID := ids.NodeID(newValidatorKey.PublicKey().Address()) + newValidatorID := ids.GenerateTestNodeID() newValidatorStartTime := uint64(defaultValidateStartTime.Add(5 * time.Second).Unix()) newValidatorEndTime := uint64(defaultValidateEndTime.Add(-5 * time.Second).Unix()) @@ -53,13 +45,11 @@ func TestAddDelegatorTxExecute(t *testing.T) { t.Fatal(err) } - staker := state.NewPrimaryNetworkStaker( + staker := state.NewCurrentStaker( tx.ID(), - &tx.Unsigned.(*txs.AddValidatorTx).Validator, + tx.Unsigned.(*txs.AddValidatorTx), + 0, ) - staker.PotentialReward = 0 - staker.NextTime = staker.EndTime - staker.Priority = state.PrimaryNetworkValidatorCurrentPriority target.state.PutCurrentValidator(staker) target.state.AddTx(tx, status.Committed) @@ -86,13 +76,11 @@ func TestAddDelegatorTxExecute(t *testing.T) { t.Fatal(err) } - staker := state.NewPrimaryNetworkStaker( + staker := state.NewCurrentStaker( tx.ID(), - &tx.Unsigned.(*txs.AddValidatorTx).Validator, + tx.Unsigned.(*txs.AddValidatorTx), + 0, ) - staker.PotentialReward = 0 - staker.NextTime = staker.EndTime - staker.Priority = state.PrimaryNetworkValidatorCurrentPriority target.state.PutCurrentValidator(staker) target.state.AddTx(tx, status.Committed) @@ -294,13 +282,36 @@ func TestAddDelegatorTxExecute(t *testing.T) { tt.setup(freshTH) } + onCommitState, err := state.NewDiff(lastAcceptedID, freshTH) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, freshTH) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, + Backend: &freshTH.backend, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err != nil && !tt.shouldErr { + t.Fatalf("shouldn't have errored but got %s", err) + } else if err == nil && tt.shouldErr { + t.Fatalf("expected test to error but got none") + } + + mempoolExecutor := MempoolTxVerifier{ Backend: &freshTH.backend, ParentID: lastAcceptedID, StateVersions: freshTH, Tx: tx, } - err = tx.Unsigned.Visit(&executor) + err = tx.Unsigned.Visit(&mempoolExecutor) if err != nil && !tt.shouldErr { t.Fatalf("shouldn't have errored but got %s", err) } else if err == nil && tt.shouldErr { @@ -310,7 +321,7 @@ func TestAddDelegatorTxExecute(t *testing.T) { } } -func TestAddSubnetValidatorTxExecute(t *testing.T) { +func TestProposalTxExecuteAddSubnetValidator(t *testing.T) { env := newEnvironment() env.ctx.Lock.Lock() defer func() { @@ -338,10 +349,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -368,10 +389,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -420,10 +451,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -432,13 +473,11 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { } } - staker := state.NewPrimaryNetworkStaker( + staker := state.NewCurrentStaker( addDSTx.ID(), - &addDSTx.Unsigned.(*txs.AddValidatorTx).Validator, + addDSTx.Unsigned.(*txs.AddValidatorTx), + 0, ) - staker.PotentialReward = 0 - staker.NextTime = staker.EndTime - staker.Priority = state.PrimaryNetworkValidatorCurrentPriority env.state.PutCurrentValidator(staker) env.state.AddTx(addDSTx, status.Committed) @@ -466,10 +505,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -494,10 +543,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -522,10 +581,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -553,10 +622,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -583,12 +662,11 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } - staker = state.NewSubnetStaker( + staker = state.NewCurrentStaker( subnetTx.ID(), - &subnetTx.Unsigned.(*txs.AddSubnetValidatorTx).Validator, + subnetTx.Unsigned.(*txs.AddSubnetValidatorTx), + 0, ) - staker.NextTime = staker.EndTime - staker.Priority = state.SubnetValidatorCurrentPriority env.state.PutCurrentValidator(staker) env.state.AddTx(subnetTx, status.Committed) @@ -612,10 +690,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: duplicateSubnetTx, } err = duplicateSubnetTx.Unsigned.Visit(&executor) @@ -645,10 +733,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -679,10 +777,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { // This tx was syntactically verified when it was created...pretend it wasn't so we don't use cache addSubnetValidatorTx.SyntacticallyVerified = false + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -712,10 +820,20 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { } copy(tx.Creds[0].(*secp256k1fx.Credential).Sigs[0][:], sig) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -740,12 +858,11 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { t.Fatal(err) } - staker = state.NewSubnetStaker( + staker = state.NewCurrentStaker( subnetTx.ID(), - &subnetTx.Unsigned.(*txs.AddSubnetValidatorTx).Validator, + subnetTx.Unsigned.(*txs.AddSubnetValidatorTx), + 0, ) - staker.NextTime = staker.EndTime - staker.Priority = state.SubnetValidatorCurrentPriority env.state.PutCurrentValidator(staker) env.state.AddTx(tx, status.Committed) @@ -753,10 +870,21 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { if err := env.state.Commit(); err != nil { t.Fatal(err) } + + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -766,7 +894,7 @@ func TestAddSubnetValidatorTxExecute(t *testing.T) { } } -func TestAddValidatorTxExecute(t *testing.T) { +func TestProposalTxExecuteAddValidator(t *testing.T) { env := newEnvironment() env.ctx.Lock.Lock() defer func() { @@ -775,12 +903,7 @@ func TestAddValidatorTxExecute(t *testing.T) { } }() - factory := crypto.FactorySECP256K1R{} - key, err := factory.NewPrivateKey() - if err != nil { - t.Fatal(err) - } - nodeID := key.PublicKey().Address() + nodeID := ids.GenerateTestNodeID() { // Case: Validator's start time too early @@ -788,8 +911,8 @@ func TestAddValidatorTxExecute(t *testing.T) { env.config.MinValidatorStake, uint64(defaultValidateStartTime.Unix())-1, uint64(defaultValidateEndTime.Unix()), - ids.NodeID(nodeID), nodeID, + ids.ShortEmpty, reward.PercentDenominator, []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, ids.ShortEmpty, // change addr @@ -798,10 +921,20 @@ func TestAddValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -816,8 +949,8 @@ func TestAddValidatorTxExecute(t *testing.T) { env.config.MinValidatorStake, uint64(defaultValidateStartTime.Add(MaxFutureStartTime).Unix()+1), uint64(defaultValidateStartTime.Add(MaxFutureStartTime).Add(defaultMinStakingDuration).Unix()+1), - ids.NodeID(nodeID), nodeID, + ids.ShortEmpty, reward.PercentDenominator, []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, ids.ShortEmpty, // change addr @@ -826,10 +959,20 @@ func TestAddValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -844,8 +987,8 @@ func TestAddValidatorTxExecute(t *testing.T) { env.config.MinValidatorStake, uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateEndTime.Unix()), - ids.NodeID(nodeID), // node ID - nodeID, // reward address + nodeID, + ids.ShortEmpty, reward.PercentDenominator, []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, ids.ShortEmpty, // change addr @@ -854,10 +997,20 @@ func TestAddValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -868,32 +1021,26 @@ func TestAddValidatorTxExecute(t *testing.T) { { // Case: Validator in pending validator set of primary network - key2, err := testKeyfactory.NewPrivateKey() - if err != nil { - t.Fatal(err) - } startTime := defaultGenesisTime.Add(1 * time.Second) tx, err := env.txBuilder.NewAddValidatorTx( env.config.MinValidatorStake, // stake amount uint64(startTime.Unix()), // start time uint64(startTime.Add(defaultMinStakingDuration).Unix()), // end time - ids.NodeID(nodeID), // node ID - key2.PublicKey().Address(), // reward address - reward.PercentDenominator, // shares + nodeID, + ids.ShortEmpty, + reward.PercentDenominator, // shares []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, - ids.ShortEmpty, // change addr // key + ids.ShortEmpty, // change addr ) if err != nil { t.Fatal(err) } - staker := state.NewPrimaryNetworkStaker( + staker := state.NewCurrentStaker( tx.ID(), - &tx.Unsigned.(*txs.AddValidatorTx).Validator, + tx.Unsigned.(*txs.AddValidatorTx), + 0, ) - staker.PotentialReward = 0 - staker.NextTime = staker.EndTime - staker.Priority = state.PrimaryNetworkValidatorCurrentPriority env.state.PutCurrentValidator(staker) env.state.AddTx(tx, status.Committed) @@ -903,10 +1050,20 @@ func TestAddValidatorTxExecute(t *testing.T) { t.Fatal(err) } + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) @@ -921,8 +1078,8 @@ func TestAddValidatorTxExecute(t *testing.T) { env.config.MinValidatorStake, uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateEndTime.Unix()), - ids.NodeID(nodeID), nodeID, + ids.ShortEmpty, reward.PercentDenominator, []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, ids.ShortEmpty, // change addr @@ -940,73 +1097,25 @@ func TestAddValidatorTxExecute(t *testing.T) { env.state.DeleteUTXO(utxoID) } - executor := ProposalTxExecutor{ - Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, - Tx: tx, - } - err = tx.Unsigned.Visit(&executor) - if err == nil { - t.Fatal("should have failed because tx fee paying key has no funds") - } - } -} - -func TestBlueberryAddValidatorTxEmptyNodeID(t *testing.T) { - env := newEnvironment() - env.ctx.Lock.Lock() - defer func() { - if err := shutdownEnvironment(env); err != nil { + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { t.Fatal(err) } - }() - - chainTime := env.state.GetTimestamp() - startTime := defaultGenesisTime.Add(1 * time.Second) - tests := []struct { - blueberryTime time.Time - expectedError error - }{ - { // Case: Before blueberry - blueberryTime: chainTime.Add(1), - expectedError: nil, - }, - { // Case: At blueberry - blueberryTime: chainTime, - expectedError: errEmptyNodeID, - }, - { // Case: After blueberry - blueberryTime: chainTime.Add(-1), - expectedError: errEmptyNodeID, - }, - } - for _, test := range tests { - // Case: Empty validator node ID after blueberry - env.config.BlueberryTime = test.blueberryTime - - tx, err := env.txBuilder.NewAddValidatorTx( // create the tx - env.config.MinValidatorStake, - uint64(startTime.Unix()), - uint64(defaultValidateEndTime.Unix()), - ids.EmptyNodeID, - ids.GenerateTestShortID(), - reward.PercentDenominator, - []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, - ids.ShortEmpty, // change addr - ) + onAbortState, err := state.NewDiff(lastAcceptedID, env) if err != nil { t.Fatal(err) } executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&executor) - require.ErrorIs(t, err, test.expectedError) + if err == nil { + t.Fatal("should have failed because tx fee paying key has no funds") + } } } diff --git a/avalanchego/vms/platformvm/txs/executor/reward_validator_test.go b/avalanchego/vms/platformvm/txs/executor/reward_validator_test.go index 6e518041..39da0656 100644 --- a/avalanchego/vms/platformvm/txs/executor/reward_validator_test.go +++ b/avalanchego/vms/platformvm/txs/executor/reward_validator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -44,10 +44,20 @@ func TestRewardValidatorTxExecuteOnCommit(t *testing.T) { tx, err := env.txBuilder.NewRewardValidatorTx(stakerToRemove.TxID) require.NoError(err) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + txExecutor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.Error(tx.Unsigned.Visit(&txExecutor)) @@ -59,10 +69,20 @@ func TestRewardValidatorTxExecuteOnCommit(t *testing.T) { tx, err = env.txBuilder.NewRewardValidatorTx(ids.GenerateTestID()) require.NoError(err) + onCommitState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + txExecutor = ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.Error(tx.Unsigned.Visit(&txExecutor)) @@ -71,15 +91,25 @@ func TestRewardValidatorTxExecuteOnCommit(t *testing.T) { tx, err = env.txBuilder.NewRewardValidatorTx(stakerToRemove.TxID) require.NoError(err) + onCommitState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + txExecutor = ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.NoError(tx.Unsigned.Visit(&txExecutor)) - onCommitStakerIterator, err := txExecutor.OnCommit.GetCurrentStakerIterator() + onCommitStakerIterator, err := txExecutor.OnCommitState.GetCurrentStakerIterator() require.NoError(err) require.True(onCommitStakerIterator.Next()) @@ -88,13 +118,13 @@ func TestRewardValidatorTxExecuteOnCommit(t *testing.T) { require.NotEqual(stakerToRemove.TxID, nextToRemove.TxID) // check that stake/reward is given back - stakeOwners := stakerToRemoveTx.Stake[0].Out.(*secp256k1fx.TransferOutput).AddressesSet() + stakeOwners := stakerToRemoveTx.StakeOuts[0].Out.(*secp256k1fx.TransferOutput).AddressesSet() // Get old balances oldBalance, err := avax.GetBalance(env.state, stakeOwners) require.NoError(err) - txExecutor.OnCommit.Apply(env.state) + txExecutor.OnCommitState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(env.state.Commit()) @@ -126,10 +156,20 @@ func TestRewardValidatorTxExecuteOnAbort(t *testing.T) { tx, err := env.txBuilder.NewRewardValidatorTx(stakerToRemove.TxID) require.NoError(err) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + txExecutor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.Error(tx.Unsigned.Visit(&txExecutor)) @@ -142,9 +182,9 @@ func TestRewardValidatorTxExecuteOnAbort(t *testing.T) { require.NoError(err) txExecutor = ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.Error(tx.Unsigned.Visit(&txExecutor)) @@ -153,15 +193,25 @@ func TestRewardValidatorTxExecuteOnAbort(t *testing.T) { tx, err = env.txBuilder.NewRewardValidatorTx(stakerToRemove.TxID) require.NoError(err) + onCommitState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err = state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + txExecutor = ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } require.NoError(tx.Unsigned.Visit(&txExecutor)) - onAbortStakerIterator, err := txExecutor.OnAbort.GetCurrentStakerIterator() + onAbortStakerIterator, err := txExecutor.OnAbortState.GetCurrentStakerIterator() require.NoError(err) require.True(onAbortStakerIterator.Next()) @@ -170,13 +220,13 @@ func TestRewardValidatorTxExecuteOnAbort(t *testing.T) { require.NotEqual(stakerToRemove.TxID, nextToRemove.TxID) // check that stake/reward isn't given back - stakeOwners := stakerToRemoveTx.Stake[0].Out.(*secp256k1fx.TransferOutput).AddressesSet() + stakeOwners := stakerToRemoveTx.StakeOuts[0].Out.(*secp256k1fx.TransferOutput).AddressesSet() // Get old balances oldBalance, err := avax.GetBalance(env.state, stakeOwners) require.NoError(err) - txExecutor.OnAbort.Apply(env.state) + txExecutor.OnAbortState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(env.state.Commit()) @@ -185,130 +235,6 @@ func TestRewardValidatorTxExecuteOnAbort(t *testing.T) { require.Equal(oldBalance+stakerToRemove.Weight, onAbortBalance) } -func TestRewardDelegatorTxExecuteOnCommit(t *testing.T) { - require := require.New(t) - env := newEnvironment() - defer func() { - if err := shutdownEnvironment(env); err != nil { - t.Fatal(err) - } - }() - dummyHeight := uint64(1) - - vdrRewardAddress := ids.GenerateTestShortID() - delRewardAddress := ids.GenerateTestShortID() - - vdrStartTime := uint64(defaultValidateStartTime.Unix()) + 1 - vdrEndTime := uint64(defaultValidateStartTime.Add(2 * defaultMinStakingDuration).Unix()) - vdrNodeID := ids.GenerateTestNodeID() - - vdrTx, err := env.txBuilder.NewAddValidatorTx( - env.config.MinValidatorStake, // stakeAmt - vdrStartTime, - vdrEndTime, - vdrNodeID, // node ID - vdrRewardAddress, // reward address - reward.PercentDenominator/4, - []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, - ids.ShortEmpty, - ) - require.NoError(err) - - delStartTime := vdrStartTime - delEndTime := vdrEndTime - - delTx, err := env.txBuilder.NewAddDelegatorTx( - env.config.MinDelegatorStake, - delStartTime, - delEndTime, - vdrNodeID, - delRewardAddress, - []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, - ids.ShortEmpty, // Change address - ) - require.NoError(err) - - vdrStaker := state.NewPrimaryNetworkStaker( - vdrTx.ID(), - &vdrTx.Unsigned.(*txs.AddValidatorTx).Validator, - ) - vdrStaker.PotentialReward = 0 - vdrStaker.NextTime = vdrStaker.EndTime - vdrStaker.Priority = state.PrimaryNetworkValidatorCurrentPriority - - delStaker := state.NewPrimaryNetworkStaker( - delTx.ID(), - &delTx.Unsigned.(*txs.AddDelegatorTx).Validator, - ) - delStaker.PotentialReward = 1000000 - delStaker.NextTime = delStaker.EndTime - delStaker.Priority = state.PrimaryNetworkDelegatorCurrentPriority - - env.state.PutCurrentValidator(vdrStaker) - env.state.AddTx(vdrTx, status.Committed) - env.state.PutCurrentDelegator(delStaker) - env.state.AddTx(delTx, status.Committed) - env.state.SetTimestamp(time.Unix(int64(delEndTime), 0)) - env.state.SetHeight(dummyHeight) - require.NoError(env.state.Commit()) - - // test validator stake - set, ok := env.config.Validators.GetValidators(constants.PrimaryNetworkID) - require.True(ok) - stake, ok := set.GetWeight(vdrNodeID) - require.True(ok) - require.Equal(env.config.MinValidatorStake+env.config.MinDelegatorStake, stake) - - tx, err := env.txBuilder.NewRewardValidatorTx(delTx.ID()) - require.NoError(err) - - txExecutor := ProposalTxExecutor{ - Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, - Tx: tx, - } - err = tx.Unsigned.Visit(&txExecutor) - require.NoError(err) - - vdrDestSet := ids.ShortSet{} - vdrDestSet.Add(vdrRewardAddress) - delDestSet := ids.ShortSet{} - delDestSet.Add(delRewardAddress) - - expectedReward := uint64(0) - - oldVdrBalance, err := avax.GetBalance(env.state, vdrDestSet) - require.NoError(err) - oldDelBalance, err := avax.GetBalance(env.state, delDestSet) - require.NoError(err) - - txExecutor.OnCommit.Apply(env.state) - env.state.SetHeight(dummyHeight) - require.NoError(env.state.Commit()) - - // If tx is committed, delegator and delegatee should get reward - // and the delegator's reward should be greater because the delegatee's share is 25% - commitVdrBalance, err := avax.GetBalance(env.state, vdrDestSet) - require.NoError(err) - vdrReward, err := math.Sub64(commitVdrBalance, oldVdrBalance) - require.NoError(err) - require.Zero(vdrReward, "expected delegatee balance to be zero") - - commitDelBalance, err := avax.GetBalance(env.state, delDestSet) - require.NoError(err) - delReward, err := math.Sub64(commitDelBalance, oldDelBalance) - require.NoError(err) - require.Zero(delReward, "expected delegator balance to be zero") - - require.Equal(vdrReward, delReward, "the delegator's reward should be greater than the delegatee's because the delegatee's share is 25%") - require.Equal(expectedReward, delReward+vdrReward, "expected total reward to be %d but is %d", expectedReward, delReward+vdrReward) - - stake, ok = set.GetWeight(vdrNodeID) - require.True(ok) - require.Equal(env.config.MinValidatorStake, stake) -} - func TestRewardDelegatorTxExecuteOnAbort(t *testing.T) { require := require.New(t) env := newEnvironment() @@ -319,7 +245,8 @@ func TestRewardDelegatorTxExecuteOnAbort(t *testing.T) { }() dummyHeight := uint64(1) - initialSupply := env.state.GetCurrentSupply() + initialSupply, err := env.state.GetCurrentSupply(constants.PrimaryNetworkID) + require.NoError(err) vdrRewardAddress := ids.GenerateTestShortID() delRewardAddress := ids.GenerateTestShortID() @@ -353,21 +280,17 @@ func TestRewardDelegatorTxExecuteOnAbort(t *testing.T) { ) require.NoError(err) - vdrStaker := state.NewPrimaryNetworkStaker( + vdrStaker := state.NewCurrentStaker( vdrTx.ID(), - &vdrTx.Unsigned.(*txs.AddValidatorTx).Validator, + vdrTx.Unsigned.(*txs.AddValidatorTx), + 0, ) - vdrStaker.PotentialReward = 0 - vdrStaker.NextTime = vdrStaker.EndTime - vdrStaker.Priority = state.PrimaryNetworkValidatorCurrentPriority - delStaker := state.NewPrimaryNetworkStaker( + delStaker := state.NewCurrentStaker( delTx.ID(), - &delTx.Unsigned.(*txs.AddDelegatorTx).Validator, + delTx.Unsigned.(*txs.AddDelegatorTx), + 1000000, ) - delStaker.PotentialReward = 1000000 - delStaker.NextTime = delStaker.EndTime - delStaker.Priority = state.PrimaryNetworkDelegatorCurrentPriority env.state.PutCurrentValidator(vdrStaker) env.state.AddTx(vdrTx, status.Committed) @@ -380,10 +303,20 @@ func TestRewardDelegatorTxExecuteOnAbort(t *testing.T) { tx, err := env.txBuilder.NewRewardValidatorTx(delTx.ID()) require.NoError(err) + onCommitState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + onAbortState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + txExecutor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: &env.backend, - ParentID: lastAcceptedID, - StateVersions: env, Tx: tx, } err = tx.Unsigned.Visit(&txExecutor) @@ -401,7 +334,7 @@ func TestRewardDelegatorTxExecuteOnAbort(t *testing.T) { oldDelBalance, err := avax.GetBalance(env.state, delDestSet) require.NoError(err) - txExecutor.OnAbort.Apply(env.state) + txExecutor.OnAbortState.Apply(env.state) env.state.SetHeight(dummyHeight) require.NoError(env.state.Commit()) @@ -418,6 +351,7 @@ func TestRewardDelegatorTxExecuteOnAbort(t *testing.T) { require.NoError(err) require.Zero(delReward, "expected delegator balance not to increase") - newSupply := env.state.GetCurrentSupply() + newSupply, err := env.state.GetCurrentSupply(constants.PrimaryNetworkID) + require.NoError(err) require.Equal(initialSupply-expectedReward, newSupply, "should have removed un-rewarded tokens from the potential supply") } diff --git a/avalanchego/vms/platformvm/txs/executor/staker_tx_verification.go b/avalanchego/vms/platformvm/txs/executor/staker_tx_verification.go new file mode 100644 index 00000000..c1c84c64 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/executor/staker_tx_verification.go @@ -0,0 +1,804 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "errors" + "fmt" + "time" + + stdmath "math" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +var ( + errWeightTooSmall = errors.New("weight of this validator is too low") + errWeightTooLarge = errors.New("weight of this validator is too large") + errInsufficientDelegationFee = errors.New("staker charges an insufficient delegation fee") + errStakeTooShort = errors.New("staking period is too short") + errStakeTooLong = errors.New("staking period is too long") + errFlowCheckFailed = errors.New("flow check failed") + errFutureStakeTime = fmt.Errorf("staker is attempting to start staking more than %s ahead of the current chain time", MaxFutureStartTime) + errValidatorSubset = errors.New("all subnets' staking period must be a subset of the primary network") + errNotValidator = errors.New("isn't a current or pending validator") + errRemovePermissionlessValidator = errors.New("attempting to remove permissionless validator") + errStakeOverflow = errors.New("validator stake exceeds limit") + errOverDelegated = errors.New("validator would be over delegated") + errIsNotTransformSubnetTx = errors.New("is not a transform subnet tx") + errTimestampNotBeforeStartTime = errors.New("chain timestamp not before start time") + errDuplicateValidator = errors.New("duplicate validator") + errDelegateToPermissionedValidator = errors.New("delegation to permissioned validator") + errWrongStakedAssetID = errors.New("incorrect staked assetID") +) + +// verifyAddValidatorTx carries out the validation for an AddValidatorTx. +// It returns the tx outputs that should be returned if this validator is not +// added to the staking set. +func verifyAddValidatorTx( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + tx *txs.AddValidatorTx, +) ( + []*avax.TransferableOutput, + error, +) { + // Verify the tx is well-formed + if err := sTx.SyntacticVerify(backend.Ctx); err != nil { + return nil, err + } + + currentTimestamp := chainState.GetTimestamp() + minValidatorStake, maxValidatorStake, _, minDelegationFee, minStakeDuration, _, maxStakeDuration, minFutureStartTimeOffset, _, minStakeStartTime := GetCurrentInflationSettings(currentTimestamp, backend.Ctx.NetworkID, backend.Config) + duration := tx.Validator.Duration() + + switch { + case tx.Validator.Wght < minValidatorStake: + // Ensure validator is staking at least the minimum amount + return nil, errWeightTooSmall + + case tx.Validator.Wght > maxValidatorStake: + // Ensure validator isn't staking too much + return nil, errWeightTooLarge + + case tx.DelegationShares < minDelegationFee: + // Ensure the validator fee is at least the minimum amount + return nil, errInsufficientDelegationFee + + case duration < minStakeDuration: + // Ensure staking length is not too short + return nil, errStakeTooShort + + case duration > maxStakeDuration: + // Ensure staking length is not too long + return nil, errStakeTooLong + } + + outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) + copy(outs, tx.Outs) + copy(outs[len(tx.Outs):], tx.StakeOuts) + + if !backend.Bootstrapped.GetValue() { + return outs, nil + } + + // Ensure the proposed validator starts after the current time + startTime := tx.StartTime() + if !currentTimestamp.Before(startTime) { + return nil, fmt.Errorf( + "%w: %s >= %s", + errTimestampNotBeforeStartTime, + currentTimestamp, + startTime, + ) + } + + if !minStakeStartTime.Before(startTime) { + return nil, fmt.Errorf( + "validator's start time (%s) at or before minStakeStartTime (%s)", + startTime, + minStakeStartTime, + ) + } + + _, err := GetValidator(chainState, constants.PrimaryNetworkID, tx.Validator.NodeID) + if err == nil { + return nil, fmt.Errorf( + "attempted to issue duplicate validation for %s", + tx.Validator.NodeID, + ) + } + if err != database.ErrNotFound { + return nil, fmt.Errorf( + "failed to find whether %s is a primary network validator: %w", + tx.Validator.NodeID, + err, + ) + } + + // Verify the flowcheck + if err := backend.FlowChecker.VerifySpend( + tx, + chainState, + tx.Ins, + outs, + sTx.Creds, + map[ids.ID]uint64{ + backend.Ctx.AVAXAssetID: backend.Config.AddPrimaryNetworkValidatorFee, + }, + ); err != nil { + return nil, fmt.Errorf("%w: %s", errFlowCheckFailed, err) + } + + // Make sure the tx doesn't start too far in the future. This is done last + // to allow the verifier visitor to explicitly check for this error. + maxStartTime := currentTimestamp.Add(MaxFutureStartTime) + if startTime.After(maxStartTime) { + return nil, errFutureStakeTime + } + minStartTime := maxStartTime.Add(-minFutureStartTimeOffset) + if startTime.Before(minStartTime) { + return nil, fmt.Errorf( + "validator's start time (%s) at or before minStartTime (%s)", + startTime, + minStartTime, + ) + } + return outs, nil +} + +// verifyAddSubnetValidatorTx carries out the validation for an +// AddSubnetValidatorTx. +func verifyAddSubnetValidatorTx( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + tx *txs.AddSubnetValidatorTx, +) error { + // Verify the tx is well-formed + if err := sTx.SyntacticVerify(backend.Ctx); err != nil { + return err + } + + // Flare does not allow creation of subnets + if constants.IsFlareNetworkID(backend.Ctx.NetworkID) || constants.IsSgbNetworkID(backend.Ctx.NetworkID) { + return errWrongTxType + } + + duration := tx.Validator.Duration() + switch { + case duration < backend.Config.MinStakeDuration: + // Ensure staking length is not too short + return errStakeTooShort + + case duration > backend.Config.MaxStakeDuration: + // Ensure staking length is not too long + return errStakeTooLong + } + + if !backend.Bootstrapped.GetValue() { + return nil + } + + currentTimestamp := chainState.GetTimestamp() + // Ensure the proposed validator starts after the current timestamp + validatorStartTime := tx.StartTime() + if !currentTimestamp.Before(validatorStartTime) { + return fmt.Errorf( + "%w: %s >= %s", + errTimestampNotBeforeStartTime, + currentTimestamp, + validatorStartTime, + ) + } + + _, err := GetValidator(chainState, tx.Validator.Subnet, tx.Validator.NodeID) + if err == nil { + return fmt.Errorf( + "attempted to issue duplicate subnet validation for %s", + tx.Validator.NodeID, + ) + } + if err != database.ErrNotFound { + return fmt.Errorf( + "failed to find whether %s is a subnet validator: %w", + tx.Validator.NodeID, + err, + ) + } + + primaryNetworkValidator, err := GetValidator(chainState, constants.PrimaryNetworkID, tx.Validator.NodeID) + if err != nil { + return fmt.Errorf( + "failed to fetch the primary network validator for %s: %w", + tx.Validator.NodeID, + err, + ) + } + + // Ensure that the period this validator validates the specified subnet + // is a subset of the time they validate the primary network. + if !tx.Validator.BoundedBy(primaryNetworkValidator.StartTime, primaryNetworkValidator.EndTime) { + return errValidatorSubset + } + + baseTxCreds, err := verifyPoASubnetAuthorization(backend, chainState, sTx, tx.Validator.Subnet, tx.SubnetAuth) + if err != nil { + return err + } + + // Verify the flowcheck + if err := backend.FlowChecker.VerifySpend( + tx, + chainState, + tx.Ins, + tx.Outs, + baseTxCreds, + map[ids.ID]uint64{ + backend.Ctx.AVAXAssetID: backend.Config.AddSubnetValidatorFee, + }, + ); err != nil { + return fmt.Errorf("%w: %s", errFlowCheckFailed, err) + } + + // Make sure the tx doesn't start too far in the future. This is done last + // to allow the verifier visitor to explicitly check for this error. + maxStartTime := currentTimestamp.Add(MaxFutureStartTime) + if validatorStartTime.After(maxStartTime) { + return errFutureStakeTime + } + + return nil +} + +// Returns the representation of [tx.NodeID] validating [tx.Subnet]. +// Returns true if [tx.NodeID] is a current validator of [tx.Subnet]. +// Returns an error if the given tx is invalid. +// The transaction is valid if: +// * [tx.NodeID] is a current/pending PoA validator of [tx.Subnet]. +// * [sTx]'s creds authorize it to spend the stated inputs. +// * [sTx]'s creds authorize it to remove a validator from [tx.Subnet]. +// * The flow checker passes. +func removeSubnetValidatorValidation( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + tx *txs.RemoveSubnetValidatorTx, +) (*state.Staker, bool, error) { + // Verify the tx is well-formed + if err := sTx.SyntacticVerify(backend.Ctx); err != nil { + return nil, false, err + } + + isCurrentValidator := true + vdr, err := chainState.GetCurrentValidator(tx.Subnet, tx.NodeID) + if err == database.ErrNotFound { + vdr, err = chainState.GetPendingValidator(tx.Subnet, tx.NodeID) + isCurrentValidator = false + } + if err != nil { + // It isn't a current or pending validator. + return nil, false, fmt.Errorf( + "%s %w of %s: %s", + tx.NodeID, + errNotValidator, + tx.Subnet, + err, + ) + } + + if vdr.Priority != txs.SubnetPermissionedValidatorCurrentPriority && + vdr.Priority != txs.SubnetPermissionedValidatorPendingPriority { + return nil, false, errRemovePermissionlessValidator + } + + if !backend.Bootstrapped.GetValue() { + // Not bootstrapped yet -- don't need to do full verification. + return vdr, isCurrentValidator, nil + } + + baseTxCreds, err := verifySubnetAuthorization(backend, chainState, sTx, tx.Subnet, tx.SubnetAuth) + if err != nil { + return nil, false, err + } + + // Verify the flowcheck + if err := backend.FlowChecker.VerifySpend( + tx, + chainState, + tx.Ins, + tx.Outs, + baseTxCreds, + map[ids.ID]uint64{ + backend.Ctx.AVAXAssetID: backend.Config.TxFee, + }, + ); err != nil { + return nil, false, fmt.Errorf("%w: %s", errFlowCheckFailed, err) + } + + return vdr, isCurrentValidator, nil +} + +// verifyAddDelegatorTx carries out the validation for an AddDelegatorTx. +// It returns the tx outputs that should be returned if this delegator is not +// added to the staking set. +func verifyAddDelegatorTx( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + tx *txs.AddDelegatorTx, +) ( + []*avax.TransferableOutput, + error, +) { + // Verify the tx is well-formed + if err := sTx.SyntacticVerify(backend.Ctx); err != nil { + return nil, err + } + + currentTimestamp := chainState.GetTimestamp() + _, maxValidatorStake, minDelegatorStake, _, _, minStakeDuration, maxStakeDuration, minFutureStartTimeOffset, maxValidatorWeightFactor, _ := GetCurrentInflationSettings(currentTimestamp, backend.Ctx.NetworkID, backend.Config) + + duration := tx.Validator.Duration() + switch { + case duration < minStakeDuration: + // Ensure staking length is not too short + return nil, errStakeTooShort + + case duration > maxStakeDuration: + // Ensure staking length is not too long + return nil, errStakeTooLong + + case tx.Validator.Wght < minDelegatorStake: + // Ensure validator is staking at least the minimum amount + return nil, errWeightTooSmall + } + + outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) + copy(outs, tx.Outs) + copy(outs[len(tx.Outs):], tx.StakeOuts) + + if !backend.Bootstrapped.GetValue() { + return outs, nil + } + + // Ensure the proposed validator starts after the current timestamp + validatorStartTime := tx.StartTime() + if !currentTimestamp.Before(validatorStartTime) { + return nil, fmt.Errorf( + "%w: %s >= %s", + errTimestampNotBeforeStartTime, + currentTimestamp, + validatorStartTime, + ) + } + + primaryNetworkValidator, err := GetValidator(chainState, constants.PrimaryNetworkID, tx.Validator.NodeID) + if err != nil { + return nil, fmt.Errorf( + "failed to fetch the primary network validator for %s: %w", + tx.Validator.NodeID, + err, + ) + } + + maximumWeight, err := math.Mul64(maxValidatorWeightFactor, primaryNetworkValidator.Weight) + if err != nil { + return nil, errStakeOverflow + } + + if backend.Config.IsApricotPhase3Activated(currentTimestamp) { + maximumWeight = math.Min64(maximumWeight, maxValidatorStake) + } + + txID := sTx.ID() + + newStaker := state.NewPendingStaker(txID, tx) + canDelegate, err := canDelegate(chainState, primaryNetworkValidator, maximumWeight, newStaker) + if err != nil { + return nil, err + } + if !canDelegate { + return nil, errOverDelegated + } + + // Verify the flowcheck + if err := backend.FlowChecker.VerifySpend( + tx, + chainState, + tx.Ins, + outs, + sTx.Creds, + map[ids.ID]uint64{ + backend.Ctx.AVAXAssetID: backend.Config.AddPrimaryNetworkDelegatorFee, + }, + ); err != nil { + return nil, fmt.Errorf("%w: %s", errFlowCheckFailed, err) + } + + // Make sure the tx doesn't start too far in the future. This is done last + // to allow the verifier visitor to explicitly check for this error. + maxStartTime := currentTimestamp.Add(MaxFutureStartTime) + if validatorStartTime.After(maxStartTime) { + return nil, errFutureStakeTime + } + minStartTime := maxStartTime.Add(-minFutureStartTimeOffset) + if validatorStartTime.Before(minStartTime) { + return nil, fmt.Errorf( + "validator's start time (%s) at or before minStartTime (%s)", + validatorStartTime, + minStartTime, + ) + } + return outs, nil +} + +// verifyAddPermissionlessValidatorTx carries out the validation for an +// AddPermissionlessValidatorTx. +func verifyAddPermissionlessValidatorTx( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + tx *txs.AddPermissionlessValidatorTx, +) error { + // Verify the tx is well-formed + if err := sTx.SyntacticVerify(backend.Ctx); err != nil { + return err + } + + // Flare does not (yet) allow adding permissionless validator tx + if constants.IsFlareNetworkID(backend.Ctx.NetworkID) || constants.IsSgbNetworkID(backend.Ctx.NetworkID) { + return errWrongTxType + } + + if !backend.Bootstrapped.GetValue() { + return nil + } + + currentTimestamp := chainState.GetTimestamp() + // Ensure the proposed validator starts after the current time + startTime := tx.StartTime() + if !currentTimestamp.Before(startTime) { + return fmt.Errorf( + "%w: %s >= %s", + errTimestampNotBeforeStartTime, + currentTimestamp, + startTime, + ) + } + + validatorRules, err := getValidatorRules(currentTimestamp, backend, chainState, tx.Subnet) + if err != nil { + return err + } + + if !validatorRules.minStakeStartTime.Before(startTime) { + return fmt.Errorf( + "validator's start time (%s) at or before minStakeStartTime (%s)", + startTime, + validatorRules.minStakeStartTime, + ) + } + + duration := tx.Validator.Duration() + stakedAssetID := tx.StakeOuts[0].AssetID() + switch { + case tx.Validator.Wght < validatorRules.minValidatorStake: + // Ensure validator is staking at least the minimum amount + return errWeightTooSmall + + case tx.Validator.Wght > validatorRules.maxValidatorStake: + // Ensure validator isn't staking too much + return errWeightTooLarge + + case tx.DelegationShares < validatorRules.minDelegationFee: + // Ensure the validator fee is at least the minimum amount + return errInsufficientDelegationFee + + case duration < validatorRules.minStakeDuration: + // Ensure staking length is not too short + return errStakeTooShort + + case duration > validatorRules.maxStakeDuration: + // Ensure staking length is not too long + return errStakeTooLong + + case stakedAssetID != validatorRules.assetID: + // Wrong assetID used + return fmt.Errorf( + "%w: %s != %s", + errWrongStakedAssetID, + validatorRules.assetID, + stakedAssetID, + ) + } + + _, err = GetValidator(chainState, tx.Subnet, tx.Validator.NodeID) + if err == nil { + return fmt.Errorf( + "%w: %s on %s", + errDuplicateValidator, + tx.Validator.NodeID, + tx.Subnet, + ) + } + if err != database.ErrNotFound { + return fmt.Errorf( + "failed to find whether %s is a validator on %s: %w", + tx.Validator.NodeID, + tx.Subnet, + err, + ) + } + + var txFee uint64 + if tx.Subnet != constants.PrimaryNetworkID { + primaryNetworkValidator, err := GetValidator(chainState, constants.PrimaryNetworkID, tx.Validator.NodeID) + if err != nil { + return fmt.Errorf( + "failed to fetch the primary network validator for %s: %w", + tx.Validator.NodeID, + err, + ) + } + + // Ensure that the period this validator validates the specified subnet + // is a subset of the time they validate the primary network. + if !tx.Validator.BoundedBy(primaryNetworkValidator.StartTime, primaryNetworkValidator.EndTime) { + return errValidatorSubset + } + + txFee = backend.Config.AddSubnetValidatorFee + } else { + txFee = backend.Config.AddPrimaryNetworkValidatorFee + } + + outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) + copy(outs, tx.Outs) + copy(outs[len(tx.Outs):], tx.StakeOuts) + + // Verify the flowcheck + if err := backend.FlowChecker.VerifySpend( + tx, + chainState, + tx.Ins, + outs, + sTx.Creds, + map[ids.ID]uint64{ + backend.Ctx.AVAXAssetID: txFee, + }, + ); err != nil { + return fmt.Errorf("%w: %s", errFlowCheckFailed, err) + } + + // Make sure the tx doesn't start too far in the future. This is done last + // to allow the verifier visitor to explicitly check for this error. + maxStartTime := currentTimestamp.Add(MaxFutureStartTime) + if startTime.After(maxStartTime) { + return errFutureStakeTime + } + return nil +} + +type addValidatorRules struct { + assetID ids.ID + minValidatorStake uint64 + maxValidatorStake uint64 + minStakeDuration time.Duration + maxStakeDuration time.Duration + minDelegationFee uint32 + minStakeStartTime time.Time +} + +func getValidatorRules( + timestamp time.Time, + backend *Backend, + chainState state.Chain, + subnetID ids.ID, +) (*addValidatorRules, error) { + if subnetID == constants.PrimaryNetworkID { + return getCurrentValidatorRules(timestamp, backend), nil + } + + transformSubnetIntf, err := chainState.GetSubnetTransformation(subnetID) + if err != nil { + return nil, err + } + transformSubnet, ok := transformSubnetIntf.Unsigned.(*txs.TransformSubnetTx) + if !ok { + return nil, errIsNotTransformSubnetTx + } + + return &addValidatorRules{ + assetID: transformSubnet.AssetID, + minValidatorStake: transformSubnet.MinValidatorStake, + maxValidatorStake: transformSubnet.MaxValidatorStake, + minStakeDuration: time.Duration(transformSubnet.MinStakeDuration) * time.Second, + maxStakeDuration: time.Duration(transformSubnet.MaxStakeDuration) * time.Second, + minDelegationFee: transformSubnet.MinDelegationFee, + }, nil +} + +// verifyAddPermissionlessDelegatorTx carries out the validation for an +// AddPermissionlessDelegatorTx. +func verifyAddPermissionlessDelegatorTx( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + tx *txs.AddPermissionlessDelegatorTx, +) error { + // Verify the tx is well-formed + if err := sTx.SyntacticVerify(backend.Ctx); err != nil { + return err + } + + // Flare does not (yet) allow adding permissionless delegator tx + if constants.IsFlareNetworkID(backend.Ctx.NetworkID) || constants.IsSgbNetworkID(backend.Ctx.NetworkID) { + return errWrongTxType + } + + if !backend.Bootstrapped.GetValue() { + return nil + } + + currentTimestamp := chainState.GetTimestamp() + // Ensure the proposed validator starts after the current timestamp + startTime := tx.StartTime() + if !currentTimestamp.Before(startTime) { + return fmt.Errorf( + "chain timestamp (%s) not before validator's start time (%s)", + currentTimestamp, + startTime, + ) + } + + delegatorRules, err := getDelegatorRules(currentTimestamp, backend, chainState, tx.Subnet) + if err != nil { + return err + } + + duration := tx.Validator.Duration() + stakedAssetID := tx.StakeOuts[0].AssetID() + switch { + case tx.Validator.Wght < delegatorRules.minDelegatorStake: + // Ensure delegator is staking at least the minimum amount + return errWeightTooSmall + + case duration < delegatorRules.minStakeDuration: + // Ensure staking length is not too short + return errStakeTooShort + + case duration > delegatorRules.maxStakeDuration: + // Ensure staking length is not too long + return errStakeTooLong + + case stakedAssetID != delegatorRules.assetID: + // Wrong assetID used + return fmt.Errorf( + "%w: %s != %s", + errWrongStakedAssetID, + delegatorRules.assetID, + stakedAssetID, + ) + } + + validator, err := GetValidator(chainState, tx.Subnet, tx.Validator.NodeID) + if err != nil { + return fmt.Errorf( + "failed to fetch the validator for %s on %s: %w", + tx.Validator.NodeID, + tx.Subnet, + err, + ) + } + + maximumWeight, err := math.Mul64( + uint64(delegatorRules.maxValidatorWeightFactor), + validator.Weight, + ) + if err != nil { + maximumWeight = stdmath.MaxUint64 + } + maximumWeight = math.Min64(maximumWeight, delegatorRules.maxValidatorStake) + + txID := sTx.ID() + newStaker := state.NewPendingStaker(txID, tx) + canDelegate, err := canDelegate(chainState, validator, maximumWeight, newStaker) + if err != nil { + return err + } + if !canDelegate { + return errOverDelegated + } + + outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) + copy(outs, tx.Outs) + copy(outs[len(tx.Outs):], tx.StakeOuts) + + var txFee uint64 + if tx.Subnet != constants.PrimaryNetworkID { + // Invariant: Delegators must only be able to reference validator + // transactions that implement [txs.ValidatorTx]. All + // validator transactions implement this interface except the + // AddSubnetValidatorTx. AddSubnetValidatorTx is the only + // permissioned validator, so we verify this delegator is + // pointing to a permissionless validator. + if validator.Priority == txs.SubnetPermissionedValidatorCurrentPriority || + validator.Priority == txs.SubnetPermissionedValidatorPendingPriority { + return errDelegateToPermissionedValidator + } + + txFee = backend.Config.AddSubnetDelegatorFee + } else { + txFee = backend.Config.AddPrimaryNetworkDelegatorFee + } + + // Verify the flowcheck + if err := backend.FlowChecker.VerifySpend( + tx, + chainState, + tx.Ins, + outs, + sTx.Creds, + map[ids.ID]uint64{ + backend.Ctx.AVAXAssetID: txFee, + }, + ); err != nil { + return fmt.Errorf("%w: %s", errFlowCheckFailed, err) + } + + // Make sure the tx doesn't start too far in the future. This is done last + // to allow the verifier visitor to explicitly check for this error. + maxStartTime := currentTimestamp.Add(MaxFutureStartTime) + if startTime.After(maxStartTime) { + return errFutureStakeTime + } + + return nil +} + +type addDelegatorRules struct { + assetID ids.ID + minDelegatorStake uint64 + maxValidatorStake uint64 + minStakeDuration time.Duration + maxStakeDuration time.Duration + maxValidatorWeightFactor byte +} + +func getDelegatorRules( + timestamp time.Time, + backend *Backend, + chainState state.Chain, + subnetID ids.ID, +) (*addDelegatorRules, error) { + if subnetID == constants.PrimaryNetworkID { + return getCurrentDelegatorRules(timestamp, backend), nil + } + + transformSubnetIntf, err := chainState.GetSubnetTransformation(subnetID) + if err != nil { + return nil, err + } + transformSubnet, ok := transformSubnetIntf.Unsigned.(*txs.TransformSubnetTx) + if !ok { + return nil, errIsNotTransformSubnetTx + } + + return &addDelegatorRules{ + assetID: transformSubnet.AssetID, + minDelegatorStake: transformSubnet.MinDelegatorStake, + maxValidatorStake: transformSubnet.MaxValidatorStake, + minStakeDuration: time.Duration(transformSubnet.MinStakeDuration) * time.Second, + maxStakeDuration: time.Duration(transformSubnet.MaxStakeDuration) * time.Second, + maxValidatorWeightFactor: transformSubnet.MaxValidatorWeightFactor, + }, nil +} diff --git a/avalanchego/vms/platformvm/txs/executor/staker_tx_verification_test.go b/avalanchego/vms/platformvm/txs/executor/staker_tx_verification_test.go new file mode 100644 index 00000000..a7a178f5 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/executor/staker_tx_verification_test.go @@ -0,0 +1,758 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "errors" + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/config" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/utxo" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +func TestVerifyAddPermissionlessValidatorTx(t *testing.T) { + type test struct { + name string + backendF func(*gomock.Controller) *Backend + stateF func(*gomock.Controller) state.Chain + sTxF func() *txs.Tx + txF func() *txs.AddPermissionlessValidatorTx + expectedErr error + } + + var ( + subnetID = ids.GenerateTestID() + customAssetID = ids.GenerateTestID() + unsignedTransformTx = &txs.TransformSubnetTx{ + AssetID: customAssetID, + MinValidatorStake: 1, + MaxValidatorStake: 2, + MinStakeDuration: 3, + MaxStakeDuration: 4, + MinDelegationFee: 5, + } + transformTx = txs.Tx{ + Unsigned: unsignedTransformTx, + Creds: []verify.Verifiable{}, + } + // This tx already passed syntactic verification. + verifiedTx = txs.AddPermissionlessValidatorTx{ + BaseTx: txs.BaseTx{ + SyntacticallyVerified: true, + BaseTx: avax.BaseTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + Outs: []*avax.TransferableOutput{}, + Ins: []*avax.TransferableInput{}, + }, + }, + Validator: validator.Validator{ + NodeID: ids.GenerateTestNodeID(), + Start: 1, + End: 1 + uint64(unsignedTransformTx.MinStakeDuration), + Wght: unsignedTransformTx.MinValidatorStake, + }, + Subnet: subnetID, + StakeOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: customAssetID, + }, + }, + }, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + Threshold: 1, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + Threshold: 1, + }, + DelegationShares: 20_000, + } + verifiedSignedTx = txs.Tx{ + Unsigned: &verifiedTx, + Creds: []verify.Verifiable{}, + } + ) + verifiedSignedTx.Initialize([]byte{1}, []byte{2}) + + tests := []test{ + { + name: "fail syntactic verification", + backendF: func(*gomock.Controller) *Backend { + return &Backend{ + Ctx: snow.DefaultContextTest(), + } + }, + stateF: func(*gomock.Controller) state.Chain { + return nil + }, + sTxF: func() *txs.Tx { return nil }, + txF: func() *txs.AddPermissionlessValidatorTx { return nil }, + expectedErr: txs.ErrNilSignedTx, + }, + { + name: "not bootstrapped", + backendF: func(*gomock.Controller) *Backend { + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: &utils.AtomicBool{}, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + return nil + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { return nil }, + expectedErr: nil, + }, + { + name: "start time too early", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(verifiedTx.StartTime()) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { return &verifiedTx }, + expectedErr: errTimestampNotBeforeStartTime, + }, + { + name: "weight too low", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + state.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + tx := verifiedTx // Note that this copies [verifiedTx] + tx.Validator.Wght = unsignedTransformTx.MinValidatorStake - 1 + return &tx + }, + expectedErr: errWeightTooSmall, + }, + { + name: "weight too high", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + state.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + tx := verifiedTx // Note that this copies [verifiedTx] + tx.Validator.Wght = unsignedTransformTx.MaxValidatorStake + 1 + return &tx + }, + expectedErr: errWeightTooLarge, + }, + { + name: "insufficient delegation fee", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + state.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + tx := verifiedTx // Note that this copies [verifiedTx] + tx.Validator.Wght = unsignedTransformTx.MaxValidatorStake + tx.DelegationShares = unsignedTransformTx.MinDelegationFee - 1 + return &tx + }, + expectedErr: errInsufficientDelegationFee, + }, + { + name: "duration too short", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + state.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + tx := verifiedTx // Note that this copies [verifiedTx] + tx.Validator.Wght = unsignedTransformTx.MaxValidatorStake + tx.DelegationShares = unsignedTransformTx.MinDelegationFee + // Note the duration is 1 less than the minimum + tx.Validator.Start = 1 + tx.Validator.End = uint64(unsignedTransformTx.MinStakeDuration) + return &tx + }, + expectedErr: errStakeTooShort, + }, + { + name: "duration too long", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + state.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + tx := verifiedTx // Note that this copies [verifiedTx] + tx.Validator.Wght = unsignedTransformTx.MaxValidatorStake + tx.DelegationShares = unsignedTransformTx.MinDelegationFee + // Note the duration is more than the maximum + tx.Validator.Start = 1 + tx.Validator.End = 2 + uint64(unsignedTransformTx.MaxStakeDuration) + return &tx + }, + expectedErr: errStakeTooLong, + }, + { + name: "wrong assetID", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + state.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + tx := verifiedTx // Note that this copies [verifiedTx] + tx.StakeOuts = []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + }, + } + return &tx + }, + expectedErr: errWrongStakedAssetID, + }, + { + name: "duplicate validator", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + state.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + // State says validator exists + state.EXPECT().GetCurrentValidator(subnetID, verifiedTx.NodeID()).Return(nil, nil) + return state + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + return &verifiedTx + }, + expectedErr: errDuplicateValidator, + }, + { + name: "validator not subset of primary network validator", + backendF: func(*gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + return &Backend{ + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + mockState := state.NewMockChain(ctrl) + mockState.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + mockState.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + mockState.EXPECT().GetCurrentValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + mockState.EXPECT().GetPendingValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + // Validator time isn't subset of primary network validator time + primaryNetworkVdr := &state.Staker{ + StartTime: verifiedTx.StartTime().Add(time.Second), + EndTime: verifiedTx.EndTime(), + } + mockState.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, verifiedTx.NodeID()).Return(primaryNetworkVdr, nil) + return mockState + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + return &verifiedTx + }, + expectedErr: errValidatorSubset, + }, + { + name: "flow check fails", + backendF: func(ctrl *gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + + flowChecker := utxo.NewMockVerifier(ctrl) + flowChecker.EXPECT().VerifySpend( + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(errors.New("flow check failed")) + + return &Backend{ + FlowChecker: flowChecker, + Config: &config.Config{ + AddSubnetValidatorFee: 1, + }, + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + mockState := state.NewMockChain(ctrl) + mockState.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + mockState.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + mockState.EXPECT().GetCurrentValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + mockState.EXPECT().GetPendingValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + primaryNetworkVdr := &state.Staker{ + StartTime: verifiedTx.StartTime(), + EndTime: verifiedTx.EndTime(), + } + mockState.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, verifiedTx.NodeID()).Return(primaryNetworkVdr, nil) + return mockState + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + return &verifiedTx + }, + expectedErr: errFlowCheckFailed, + }, + { + name: "starts too far in the future", + backendF: func(ctrl *gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + + flowChecker := utxo.NewMockVerifier(ctrl) + flowChecker.EXPECT().VerifySpend( + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(nil) + + return &Backend{ + FlowChecker: flowChecker, + Config: &config.Config{ + AddSubnetValidatorFee: 1, + }, + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + mockState := state.NewMockChain(ctrl) + mockState.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + mockState.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + mockState.EXPECT().GetCurrentValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + mockState.EXPECT().GetPendingValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + primaryNetworkVdr := &state.Staker{ + StartTime: time.Unix(0, 0), + EndTime: mockable.MaxTime, + } + mockState.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, verifiedTx.NodeID()).Return(primaryNetworkVdr, nil) + return mockState + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + // Note this copies [verifiedTx] + tx := verifiedTx + tx.Validator.Start = uint64(MaxFutureStartTime.Seconds()) + 1 + tx.Validator.End = tx.Validator.Start + uint64(unsignedTransformTx.MinStakeDuration) + return &tx + }, + expectedErr: errFutureStakeTime, + }, + { + name: "success", + backendF: func(ctrl *gomock.Controller) *Backend { + bootstrapped := &utils.AtomicBool{} + bootstrapped.SetValue(true) + + flowChecker := utxo.NewMockVerifier(ctrl) + flowChecker.EXPECT().VerifySpend( + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(nil) + + return &Backend{ + FlowChecker: flowChecker, + Config: &config.Config{ + AddSubnetValidatorFee: 1, + }, + Ctx: snow.DefaultContextTest(), + Bootstrapped: bootstrapped, + } + }, + stateF: func(ctrl *gomock.Controller) state.Chain { + mockState := state.NewMockChain(ctrl) + mockState.EXPECT().GetTimestamp().Return(time.Unix(0, 0)) + mockState.EXPECT().GetSubnetTransformation(subnetID).Return(&transformTx, nil) + mockState.EXPECT().GetCurrentValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + mockState.EXPECT().GetPendingValidator(subnetID, verifiedTx.NodeID()).Return(nil, database.ErrNotFound) + primaryNetworkVdr := &state.Staker{ + StartTime: time.Unix(0, 0), + EndTime: mockable.MaxTime, + } + mockState.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, verifiedTx.NodeID()).Return(primaryNetworkVdr, nil) + return mockState + }, + sTxF: func() *txs.Tx { return &verifiedSignedTx }, + txF: func() *txs.AddPermissionlessValidatorTx { + return &verifiedTx + }, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + var ( + backend = tt.backendF(ctrl) + state = tt.stateF(ctrl) + sTx = tt.sTxF() + tx = tt.txF() + ) + + err := verifyAddPermissionlessValidatorTx(backend, state, sTx, tx) + require.ErrorIs(err, tt.expectedErr) + }) + } +} + +func TestGetValidatorRules(t *testing.T) { + type test struct { + name string + subnetID ids.ID + backend *Backend + chainStateF func(*gomock.Controller) state.Chain + expectedRules *addValidatorRules + expectedErr error + } + + var ( + config = &config.Config{ + MinValidatorStake: 1, + MaxValidatorStake: 2, + MinStakeDuration: time.Second, + MaxStakeDuration: 2 * time.Second, + MinDelegationFee: 1337, + } + avaxAssetID = ids.GenerateTestID() + customAssetID = ids.GenerateTestID() + subnetID = ids.GenerateTestID() + testErr = errors.New("an error") + ) + + tests := []test{ + { + name: "primary network", + subnetID: constants.PrimaryNetworkID, + backend: &Backend{ + Config: config, + Ctx: &snow.Context{ + AVAXAssetID: avaxAssetID, + }, + }, + chainStateF: func(*gomock.Controller) state.Chain { + return nil + }, + expectedRules: &addValidatorRules{ + assetID: avaxAssetID, + minValidatorStake: config.MinValidatorStake, + maxValidatorStake: config.MaxValidatorStake, + minStakeDuration: config.MinStakeDuration, + maxStakeDuration: config.MaxStakeDuration, + minDelegationFee: config.MinDelegationFee, + minStakeStartTime: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + { + name: "can't get subnet transformation", + subnetID: subnetID, + backend: nil, + chainStateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetSubnetTransformation(subnetID).Return(nil, testErr) + return state + }, + expectedRules: &addValidatorRules{}, + expectedErr: testErr, + }, + { + name: "invalid transformation tx", + subnetID: subnetID, + backend: nil, + chainStateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + tx := &txs.Tx{ + Unsigned: &txs.AddDelegatorTx{}, + } + state.EXPECT().GetSubnetTransformation(subnetID).Return(tx, nil) + return state + }, + expectedRules: &addValidatorRules{}, + expectedErr: errIsNotTransformSubnetTx, + }, + { + name: "subnet", + subnetID: subnetID, + backend: nil, + chainStateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + tx := &txs.Tx{ + Unsigned: &txs.TransformSubnetTx{ + AssetID: customAssetID, + MinValidatorStake: config.MinValidatorStake, + MaxValidatorStake: config.MaxValidatorStake, + MinStakeDuration: 1337, + MaxStakeDuration: 42, + MinDelegationFee: config.MinDelegationFee, + }, + } + state.EXPECT().GetSubnetTransformation(subnetID).Return(tx, nil) + return state + }, + expectedRules: &addValidatorRules{ + assetID: customAssetID, + minValidatorStake: config.MinValidatorStake, + maxValidatorStake: config.MaxValidatorStake, + minStakeDuration: time.Duration(1337) * time.Second, + maxStakeDuration: time.Duration(42) * time.Second, + minDelegationFee: config.MinDelegationFee, + }, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + chainState := tt.chainStateF(ctrl) + rules, err := getValidatorRules(time.Time{}, tt.backend, chainState, tt.subnetID) + if tt.expectedErr != nil { + require.ErrorIs(tt.expectedErr, err) + return + } + require.NoError(err) + require.Equal(tt.expectedRules, rules) + }) + } +} + +func TestGetDelegatorRules(t *testing.T) { + type test struct { + name string + subnetID ids.ID + backend *Backend + chainStateF func(*gomock.Controller) state.Chain + expectedRules *addDelegatorRules + expectedErr error + } + var ( + config = &config.Config{ + MinDelegatorStake: 1, + MaxValidatorStake: 2, + MinStakeDuration: time.Second, + MaxStakeDuration: 2 * time.Second, + } + avaxAssetID = ids.GenerateTestID() + customAssetID = ids.GenerateTestID() + subnetID = ids.GenerateTestID() + testErr = errors.New("an error") + ) + tests := []test{ + { + name: "primary network", + subnetID: constants.PrimaryNetworkID, + backend: &Backend{ + Config: config, + Ctx: &snow.Context{ + AVAXAssetID: avaxAssetID, + }, + }, + chainStateF: func(*gomock.Controller) state.Chain { + return nil + }, + expectedRules: &addDelegatorRules{ + assetID: avaxAssetID, + minDelegatorStake: config.MinDelegatorStake, + maxValidatorStake: config.MaxValidatorStake, + minStakeDuration: config.MinStakeDuration, + maxStakeDuration: config.MaxStakeDuration, + maxValidatorWeightFactor: MaxValidatorWeightFactor, + }, + }, + { + name: "can't get subnet transformation", + subnetID: subnetID, + backend: nil, + chainStateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + state.EXPECT().GetSubnetTransformation(subnetID).Return(nil, testErr) + return state + }, + expectedRules: &addDelegatorRules{}, + expectedErr: testErr, + }, + { + name: "invalid transformation tx", + subnetID: subnetID, + backend: nil, + chainStateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + tx := &txs.Tx{ + Unsigned: &txs.AddDelegatorTx{}, + } + state.EXPECT().GetSubnetTransformation(subnetID).Return(tx, nil) + return state + }, + expectedRules: &addDelegatorRules{}, + expectedErr: errIsNotTransformSubnetTx, + }, + { + name: "subnet", + subnetID: subnetID, + backend: nil, + chainStateF: func(ctrl *gomock.Controller) state.Chain { + state := state.NewMockChain(ctrl) + tx := &txs.Tx{ + Unsigned: &txs.TransformSubnetTx{ + AssetID: customAssetID, + MinDelegatorStake: config.MinDelegatorStake, + MinValidatorStake: config.MinValidatorStake, + MaxValidatorStake: config.MaxValidatorStake, + MinStakeDuration: 1337, + MaxStakeDuration: 42, + MinDelegationFee: config.MinDelegationFee, + MaxValidatorWeightFactor: 21, + }, + } + state.EXPECT().GetSubnetTransformation(subnetID).Return(tx, nil) + return state + }, + expectedRules: &addDelegatorRules{ + assetID: customAssetID, + minDelegatorStake: config.MinDelegatorStake, + maxValidatorStake: config.MaxValidatorStake, + minStakeDuration: time.Duration(1337) * time.Second, + maxStakeDuration: time.Duration(42) * time.Second, + maxValidatorWeightFactor: 21, + }, + expectedErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + chainState := tt.chainStateF(ctrl) + rules, err := getDelegatorRules(time.Time{}, tt.backend, chainState, tt.subnetID) + if tt.expectedErr != nil { + require.ErrorIs(tt.expectedErr, err) + return + } + require.NoError(err) + require.Equal(tt.expectedRules, rules) + }) + } +} diff --git a/avalanchego/vms/platformvm/txs/executor/standard_tx_executor.go b/avalanchego/vms/platformvm/txs/executor/standard_tx_executor.go index dd5e7442..b62eb424 100644 --- a/avalanchego/vms/platformvm/txs/executor/standard_tx_executor.go +++ b/avalanchego/vms/platformvm/txs/executor/standard_tx_executor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -6,9 +6,9 @@ package executor import ( "errors" "fmt" + "time" "github.com/ava-labs/avalanchego/chains/atomic" - "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/verify" @@ -20,7 +20,12 @@ import ( var ( _ txs.Visitor = &StandardTxExecutor{} - errCustomAssetBeforeBlueberry = errors.New("custom assets can only be imported after the blueberry upgrade") + errEmptyNodeID = errors.New("validator nodeID cannot be empty") + errIssuedAddStakerTxBeforeBanff = errors.New("staker transaction issued before Banff") + errCustomAssetBeforeBanff = errors.New("custom assets can only be imported after Banff") + errRemoveSubnetValidatorTxBeforeBanff = errors.New("RemoveSubnetValidatorTx issued before Banff") + errTransformSubnetTxBeforeBanff = errors.New("TransformSubnetTx issued before Banff") + errMaxStakeDurationTooLarge = errors.New("max stake duration must be less than or equal to the global max stake duration") ) type StandardTxExecutor struct { @@ -35,11 +40,6 @@ type StandardTxExecutor struct { AtomicRequests map[ids.ID]*atomic.Requests // may be nil } -func (*StandardTxExecutor) AddValidatorTx(*txs.AddValidatorTx) error { return errWrongTxType } -func (*StandardTxExecutor) AddSubnetValidatorTx(*txs.AddSubnetValidatorTx) error { - return errWrongTxType -} -func (*StandardTxExecutor) AddDelegatorTx(*txs.AddDelegatorTx) error { return errWrongTxType } func (*StandardTxExecutor) AdvanceTimeTx(*txs.AdvanceTimeTx) error { return errWrongTxType } func (*StandardTxExecutor) RewardValidatorTx(*txs.RewardValidatorTx) error { return errWrongTxType } @@ -48,17 +48,11 @@ func (e *StandardTxExecutor) CreateChainTx(tx *txs.CreateChainTx) error { return err } - // Make sure this transaction has at least one credential for the subnet - // authorization. - if len(e.Tx.Creds) == 0 { - return errWrongNumberOfCredentials + baseTxCreds, err := verifyPoASubnetAuthorization(e.Backend, e.State, e.Tx, tx.SubnetID, tx.SubnetAuth) + if err != nil { + return err } - // Select the credentials for each purpose - baseTxCredsLen := len(e.Tx.Creds) - 1 - baseTxCreds := e.Tx.Creds[:baseTxCredsLen] - subnetCred := e.Tx.Creds[baseTxCredsLen] - // Verify the flowcheck timestamp := e.State.GetTimestamp() createBlockchainTxFee := e.Config.GetCreateBlockchainTxFee(timestamp) @@ -75,24 +69,6 @@ func (e *StandardTxExecutor) CreateChainTx(tx *txs.CreateChainTx) error { return err } - subnetIntf, _, err := e.State.GetTx(tx.SubnetID) - if err == database.ErrNotFound { - return fmt.Errorf("%s isn't a known subnet", tx.SubnetID) - } - if err != nil { - return err - } - - subnet, ok := subnetIntf.Unsigned.(*txs.CreateSubnetTx) - if !ok { - return fmt.Errorf("%s isn't a subnet", tx.SubnetID) - } - - // Verify that this chain is authorized by the subnet - if err := e.Fx.VerifyPermission(tx, tx.SubnetAuth, subnetCred, subnet.Owner); err != nil { - return err - } - txID := e.Tx.ID() // Consume the UTXOS @@ -156,15 +132,15 @@ func (e *StandardTxExecutor) ImportTx(tx *txs.ImportTx) error { e.Inputs.Add(utxoID) utxoIDs[i] = utxoID[:] - if currentChainTime.Before(e.Config.BlueberryTime) { - // TODO: Remove this check once the Blueberry network upgrade is + if !e.Config.IsBanffActivated(currentChainTime) { + // TODO: Remove this check once the Banff network upgrade is // complete. // - // Blueberry network upgrade allows exporting of all assets to the + // Banff network upgrade allows exporting of all assets to the // P-chain. assetID := in.AssetID() if assetID != e.Ctx.AVAXAssetID { - return errCustomAssetBeforeBlueberry + return errCustomAssetBeforeBanff } } } @@ -297,3 +273,268 @@ func (e *StandardTxExecutor) ExportTx(tx *txs.ExportTx) error { } return nil } + +func (e *StandardTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error { + // AddValidatorTx is a proposal transaction until the Banff fork + // activation. Following the activation, AddValidatorTxs must be issued into + // StandardBlocks. + currentTimestamp := e.State.GetTimestamp() + if !e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) < Banff fork time (%s)", + errIssuedAddStakerTxBeforeBanff, + currentTimestamp, + e.Config.BanffTime, + ) + } + + if tx.Validator.NodeID == ids.EmptyNodeID { + return errEmptyNodeID + } + + if _, err := verifyAddValidatorTx( + e.Backend, + e.State, + e.Tx, + tx, + ); err != nil { + return err + } + + txID := e.Tx.ID() + + newStaker := state.NewPendingStaker(txID, tx) + e.State.PutPendingValidator(newStaker) + utxo.Consume(e.State, tx.Ins) + utxo.Produce(e.State, txID, tx.Outs) + + return nil +} + +func (e *StandardTxExecutor) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error { + // AddSubnetValidatorTx is a proposal transaction until the Banff fork + // activation. Following the activation, AddSubnetValidatorTxs must be + // issued into StandardBlocks. + currentTimestamp := e.State.GetTimestamp() + if !e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) < Banff fork time (%s)", + errIssuedAddStakerTxBeforeBanff, + currentTimestamp, + e.Config.BanffTime, + ) + } + + if err := verifyAddSubnetValidatorTx( + e.Backend, + e.State, + e.Tx, + tx, + ); err != nil { + return err + } + + txID := e.Tx.ID() + + newStaker := state.NewPendingStaker(txID, tx) + e.State.PutPendingValidator(newStaker) + utxo.Consume(e.State, tx.Ins) + utxo.Produce(e.State, txID, tx.Outs) + + return nil +} + +func (e *StandardTxExecutor) AddDelegatorTx(tx *txs.AddDelegatorTx) error { + // AddDelegatorTx is a proposal transaction until the Banff fork + // activation. Following the activation, AddDelegatorTxs must be issued into + // StandardBlocks. + currentTimestamp := e.State.GetTimestamp() + if !e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) < Banff fork time (%s)", + errIssuedAddStakerTxBeforeBanff, + currentTimestamp, + e.Config.BanffTime, + ) + } + + if _, err := verifyAddDelegatorTx( + e.Backend, + e.State, + e.Tx, + tx, + ); err != nil { + return err + } + + txID := e.Tx.ID() + newStaker := state.NewPendingStaker(txID, tx) + e.State.PutPendingDelegator(newStaker) + utxo.Consume(e.State, tx.Ins) + utxo.Produce(e.State, txID, tx.Outs) + + return nil +} + +// Verifies a [*txs.RemoveSubnetValidatorTx] and, if it passes, executes it on +// [e.State]. For verification rules, see [removeSubnetValidatorValidation]. +// This transaction will result in [tx.NodeID] being removed as a validator of +// [tx.SubnetID]. +// Note: [tx.NodeID] may be either a current or pending validator. +func (e *StandardTxExecutor) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { + currentTimestamp := e.State.GetTimestamp() + if !e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) < Banff fork time (%s)", + errRemoveSubnetValidatorTxBeforeBanff, + currentTimestamp, + e.Config.BanffTime, + ) + } + + staker, isCurrentValidator, err := removeSubnetValidatorValidation( + e.Backend, + e.State, + e.Tx, + tx, + ) + if err != nil { + return err + } + + if isCurrentValidator { + e.State.DeleteCurrentValidator(staker) + } else { + e.State.DeletePendingValidator(staker) + } + + // Invariant: There are no permissioned subnet delegators to remove. + + txID := e.Tx.ID() + utxo.Consume(e.State, tx.Ins) + utxo.Produce(e.State, txID, tx.Outs) + + return nil +} + +func (e *StandardTxExecutor) TransformSubnetTx(tx *txs.TransformSubnetTx) error { + // TODO: Remove this check once the Banff network upgrade is complete. + // + // Banff network upgrade allows transforming a permissioned subnet into + // a permissionless subnet. + currentTimestamp := e.State.GetTimestamp() + if !e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) < Banff fork time (%s)", + errTransformSubnetTxBeforeBanff, + currentTimestamp, + e.Config.BanffTime, + ) + } + + if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { + return err + } + + // Note: math.MaxInt32 * time.Second < math.MaxInt64 - so this can never + // overflow. + if time.Duration(tx.MaxStakeDuration)*time.Second > e.Backend.Config.MaxStakeDuration { + return errMaxStakeDurationTooLarge + } + + baseTxCreds, err := verifyPoASubnetAuthorization(e.Backend, e.State, e.Tx, tx.Subnet, tx.SubnetAuth) + if err != nil { + return err + } + + totalRewardAmount := tx.MaximumSupply - tx.InitialSupply + if err := e.Backend.FlowChecker.VerifySpend( + tx, + e.State, + tx.Ins, + tx.Outs, + baseTxCreds, + // Invariant: [tx.AssetID != e.Ctx.AVAXAssetID]. This prevents the first + // entry in this map literal from being overwritten by the + // second entry. + map[ids.ID]uint64{ + e.Ctx.AVAXAssetID: e.Config.TransformSubnetTxFee, + tx.AssetID: totalRewardAmount, + }, + ); err != nil { + return err + } + + txID := e.Tx.ID() + + // Consume the UTXOS + utxo.Consume(e.State, tx.Ins) + // Produce the UTXOS + utxo.Produce(e.State, txID, tx.Outs) + // Transform the new subnet in the database + e.State.AddSubnetTransformation(e.Tx) + e.State.SetCurrentSupply(tx.Subnet, tx.InitialSupply) + return nil +} + +func (e *StandardTxExecutor) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { + // TODO: Remove this check once the Banff network upgrade is complete. + currentTimestamp := e.State.GetTimestamp() + if !e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) < Banff fork time (%s)", + errIssuedAddStakerTxBeforeBanff, + currentTimestamp, + e.Config.BanffTime, + ) + } + + if err := verifyAddPermissionlessValidatorTx( + e.Backend, + e.State, + e.Tx, + tx, + ); err != nil { + return err + } + + txID := e.Tx.ID() + + newStaker := state.NewPendingStaker(txID, tx) + e.State.PutPendingValidator(newStaker) + utxo.Consume(e.State, tx.Ins) + utxo.Produce(e.State, txID, tx.Outs) + + return nil +} + +func (e *StandardTxExecutor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { + // TODO: Remove this check once the Banff network upgrade is complete. + currentTimestamp := e.State.GetTimestamp() + if !e.Config.IsBanffActivated(currentTimestamp) { + return fmt.Errorf( + "%w: timestamp (%s) < Banff fork time (%s)", + errIssuedAddStakerTxBeforeBanff, + currentTimestamp, + e.Config.BanffTime, + ) + } + + if err := verifyAddPermissionlessDelegatorTx( + e.Backend, + e.State, + e.Tx, + tx, + ); err != nil { + return err + } + + txID := e.Tx.ID() + + newStaker := state.NewPendingStaker(txID, tx) + e.State.PutPendingDelegator(newStaker) + utxo.Consume(e.State, tx.Ins) + utxo.Produce(e.State, txID, tx.Outs) + + return nil +} diff --git a/avalanchego/vms/platformvm/txs/executor/standard_tx_executor_test.go b/avalanchego/vms/platformvm/txs/executor/standard_tx_executor_test.go new file mode 100644 index 00000000..718c6f92 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/executor/standard_tx_executor_test.go @@ -0,0 +1,1808 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "errors" + "fmt" + "math" + "testing" + "time" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/config" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/status" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/utxo" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +// This tests that the math performed during TransformSubnetTx execution can +// never overflow +const _ time.Duration = math.MaxUint32 * time.Second + +func TestStandardTxExecutorAddValidatorTxEmptyID(t *testing.T) { + env := newEnvironment() + env.ctx.Lock.Lock() + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + + chainTime := env.state.GetTimestamp() + startTime := defaultGenesisTime.Add(1 * time.Second) + + tests := []struct { + banffTime time.Time + expectedError error + }{ + { // Case: Before banff + banffTime: chainTime.Add(1), + expectedError: errIssuedAddStakerTxBeforeBanff, + }, + { // Case: At banff + banffTime: chainTime, + expectedError: errEmptyNodeID, + }, + { // Case: After banff + banffTime: chainTime.Add(-1), + expectedError: errEmptyNodeID, + }, + } + for _, test := range tests { + // Case: Empty validator node ID after banff + env.config.BanffTime = test.banffTime + + tx, err := env.txBuilder.NewAddValidatorTx( // create the tx + env.config.MinValidatorStake, + uint64(startTime.Unix()), + uint64(defaultValidateEndTime.Unix()), + ids.EmptyNodeID, + ids.GenerateTestShortID(), + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + stateDiff, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: stateDiff, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + require.ErrorIs(t, err, test.expectedError) + } +} + +func TestStandardTxExecutorAddDelegator(t *testing.T) { + dummyHeight := uint64(1) + rewardAddress := preFundedKeys[0].PublicKey().Address() + nodeID := ids.NodeID(rewardAddress) + + newValidatorID := ids.GenerateTestNodeID() + newValidatorStartTime := uint64(defaultValidateStartTime.Add(5 * time.Second).Unix()) + newValidatorEndTime := uint64(defaultValidateEndTime.Add(-5 * time.Second).Unix()) + + // [addMinStakeValidator] adds a new validator to the primary network's + // pending validator set with the minimum staking amount + addMinStakeValidator := func(target *environment) { + tx, err := target.txBuilder.NewAddValidatorTx( + target.config.MinValidatorStake, // stake amount + newValidatorStartTime, // start time + newValidatorEndTime, // end time + newValidatorID, // node ID + rewardAddress, // Reward Address + reward.PercentDenominator, // Shares + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, + ) + if err != nil { + t.Fatal(err) + } + + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddValidatorTx), + 0, + ) + + target.state.PutCurrentValidator(staker) + target.state.AddTx(tx, status.Committed) + target.state.SetHeight(dummyHeight) + if err := target.state.Commit(); err != nil { + t.Fatal(err) + } + } + + // [addMaxStakeValidator] adds a new validator to the primary network's + // pending validator set with the maximum staking amount + addMaxStakeValidator := func(target *environment) { + tx, err := target.txBuilder.NewAddValidatorTx( + target.config.MaxValidatorStake, // stake amount + newValidatorStartTime, // start time + newValidatorEndTime, // end time + newValidatorID, // node ID + rewardAddress, // Reward Address + reward.PercentDenominator, // Shared + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, + ) + if err != nil { + t.Fatal(err) + } + + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddValidatorTx), + 0, + ) + + target.state.PutCurrentValidator(staker) + target.state.AddTx(tx, status.Committed) + target.state.SetHeight(dummyHeight) + if err := target.state.Commit(); err != nil { + t.Fatal(err) + } + } + + dummyH := newEnvironment() + currentTimestamp := dummyH.state.GetTimestamp() + + type test struct { + stakeAmount uint64 + startTime uint64 + endTime uint64 + nodeID ids.NodeID + rewardAddress ids.ShortID + feeKeys []*crypto.PrivateKeySECP256K1R + setup func(*environment) + AP3Time time.Time + shouldErr bool + description string + } + + tests := []test{ + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: uint64(defaultValidateStartTime.Unix()), + endTime: uint64(defaultValidateEndTime.Unix()) + 1, + nodeID: nodeID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: nil, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "validator stops validating primary network earlier than subnet", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: uint64(currentTimestamp.Add(MaxFutureStartTime + time.Second).Unix()), + endTime: uint64(currentTimestamp.Add(MaxFutureStartTime * 2).Unix()), + nodeID: nodeID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: nil, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: fmt.Sprintf("validator should not be added more than (%s) in the future", MaxFutureStartTime), + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: uint64(defaultValidateStartTime.Unix()), + endTime: uint64(defaultValidateEndTime.Unix()) + 1, + nodeID: nodeID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: nil, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "end time is after the primary network end time", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: uint64(defaultValidateStartTime.Add(5 * time.Second).Unix()), + endTime: uint64(defaultValidateEndTime.Add(-5 * time.Second).Unix()), + nodeID: newValidatorID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: nil, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "validator not in the current or pending validator sets of the subnet", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: newValidatorStartTime - 1, // start validating subnet before primary network + endTime: newValidatorEndTime, + nodeID: newValidatorID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: addMinStakeValidator, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "validator starts validating subnet before primary network", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: newValidatorStartTime, + endTime: newValidatorEndTime + 1, // stop validating subnet after stopping validating primary network + nodeID: newValidatorID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: addMinStakeValidator, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "validator stops validating primary network before subnet", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: newValidatorStartTime, // same start time as for primary network + endTime: newValidatorEndTime, // same end time as for primary network + nodeID: newValidatorID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: addMinStakeValidator, + AP3Time: defaultGenesisTime, + shouldErr: false, + description: "valid", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, // weight + startTime: uint64(currentTimestamp.Unix()), // start time + endTime: uint64(defaultValidateEndTime.Unix()), // end time + nodeID: nodeID, // node ID + rewardAddress: rewardAddress, // Reward Address + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, // tx fee payer + setup: nil, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "starts validating at current timestamp", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, // weight + startTime: uint64(defaultValidateStartTime.Unix()), // start time + endTime: uint64(defaultValidateEndTime.Unix()), // end time + nodeID: nodeID, // node ID + rewardAddress: rewardAddress, // Reward Address + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[1]}, // tx fee payer + setup: func(target *environment) { // Remove all UTXOs owned by keys[1] + utxoIDs, err := target.state.UTXOIDs( + preFundedKeys[1].PublicKey().Address().Bytes(), + ids.Empty, + math.MaxInt32) + if err != nil { + t.Fatal(err) + } + for _, utxoID := range utxoIDs { + target.state.DeleteUTXO(utxoID) + } + target.state.SetHeight(dummyHeight) + if err := target.state.Commit(); err != nil { + t.Fatal(err) + } + }, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "tx fee paying key has no funds", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: newValidatorStartTime, // same start time as for primary network + endTime: newValidatorEndTime, // same end time as for primary network + nodeID: newValidatorID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: addMaxStakeValidator, + AP3Time: defaultValidateEndTime, + shouldErr: false, + description: "over delegation before AP3", + }, + { + stakeAmount: dummyH.config.MinDelegatorStake, + startTime: newValidatorStartTime, // same start time as for primary network + endTime: newValidatorEndTime, // same end time as for primary network + nodeID: newValidatorID, + rewardAddress: rewardAddress, + feeKeys: []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + setup: addMaxStakeValidator, + AP3Time: defaultGenesisTime, + shouldErr: true, + description: "over delegation after AP3", + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + freshTH := newEnvironment() + freshTH.config.ApricotPhase3Time = tt.AP3Time + defer func() { + if err := shutdownEnvironment(freshTH); err != nil { + t.Fatal(err) + } + }() + + tx, err := freshTH.txBuilder.NewAddDelegatorTx( + tt.stakeAmount, + tt.startTime, + tt.endTime, + tt.nodeID, + tt.rewardAddress, + tt.feeKeys, + ids.ShortEmpty, + ) + if err != nil { + t.Fatalf("couldn't build tx: %s", err) + } + if tt.setup != nil { + tt.setup(freshTH) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, freshTH) + if err != nil { + t.Fatal(err) + } + + freshTH.config.BanffTime = onAcceptState.GetTimestamp() + + executor := StandardTxExecutor{ + Backend: &freshTH.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err != nil && !tt.shouldErr { + t.Fatalf("shouldn't have errored but got %s", err) + } else if err == nil && tt.shouldErr { + t.Fatalf("expected test to error but got none") + } + + mempoolExecutor := MempoolTxVerifier{ + Backend: &freshTH.backend, + ParentID: lastAcceptedID, + StateVersions: freshTH, + Tx: tx, + } + err = tx.Unsigned.Visit(&mempoolExecutor) + if err != nil && !tt.shouldErr { + t.Fatalf("shouldn't have errored but got %s", err) + } else if err == nil && tt.shouldErr { + t.Fatalf("expected test to error but got none") + } + }) + } +} + +func TestStandardTxExecutorAddSubnetValidator(t *testing.T) { + env := newEnvironment() + env.ctx.Lock.Lock() + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + + nodeID := preFundedKeys[0].PublicKey().Address() + env.config.BanffTime = env.state.GetTimestamp() + + { + // Case: Proposed validator currently validating primary network + // but stops validating subnet after stops validating primary network + // (note that keys[0] is a genesis validator) + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, + uint64(defaultValidateStartTime.Unix()), + uint64(defaultValidateEndTime.Unix())+1, + ids.NodeID(nodeID), + testSubnet1.ID(), + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed because validator stops validating primary network earlier than subnet") + } + } + + { + // Case: Proposed validator currently validating primary network + // and proposed subnet validation period is subset of + // primary network validation period + // (note that keys[0] is a genesis validator) + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, + uint64(defaultValidateStartTime.Unix()+1), + uint64(defaultValidateEndTime.Unix()), + ids.NodeID(nodeID), + testSubnet1.ID(), + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err != nil { + t.Fatal(err) + } + } + + // Add a validator to pending validator set of primary network + key, err := testKeyfactory.NewPrivateKey() + if err != nil { + t.Fatal(err) + } + pendingDSValidatorID := ids.NodeID(key.PublicKey().Address()) + + // starts validating primary network 10 seconds after genesis + DSStartTime := defaultGenesisTime.Add(10 * time.Second) + DSEndTime := DSStartTime.Add(5 * defaultMinStakingDuration) + + addDSTx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, // stake amount + uint64(DSStartTime.Unix()), // start time + uint64(DSEndTime.Unix()), // end time + pendingDSValidatorID, // node ID + nodeID, // reward address + reward.PercentDenominator, // shares + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, + ) + if err != nil { + t.Fatal(err) + } + + { + // Case: Proposed validator isn't in pending or current validator sets + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, + uint64(DSStartTime.Unix()), // start validating subnet before primary network + uint64(DSEndTime.Unix()), + pendingDSValidatorID, + testSubnet1.ID(), + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed because validator not in the current or pending validator sets of the primary network") + } + } + + staker := state.NewCurrentStaker( + addDSTx.ID(), + addDSTx.Unsigned.(*txs.AddValidatorTx), + 0, + ) + + env.state.PutCurrentValidator(staker) + env.state.AddTx(addDSTx, status.Committed) + dummyHeight := uint64(1) + env.state.SetHeight(dummyHeight) + if err := env.state.Commit(); err != nil { + t.Fatal(err) + } + + // Node with ID key.PublicKey().Address() now a pending validator for primary network + + { + // Case: Proposed validator is pending validator of primary network + // but starts validating subnet before primary network + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, + uint64(DSStartTime.Unix())-1, // start validating subnet before primary network + uint64(DSEndTime.Unix()), + pendingDSValidatorID, + testSubnet1.ID(), + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed because validator starts validating primary network before starting to validate primary network") + } + } + + { + // Case: Proposed validator is pending validator of primary network + // but stops validating subnet after primary network + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, + uint64(DSStartTime.Unix()), + uint64(DSEndTime.Unix())+1, // stop validating subnet after stopping validating primary network + pendingDSValidatorID, + testSubnet1.ID(), + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed because validator stops validating primary network after stops validating primary network") + } + } + + { + // Case: Proposed validator is pending validator of primary network and + // period validating subnet is subset of time validating primary network + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, + uint64(DSStartTime.Unix()), // same start time as for primary network + uint64(DSEndTime.Unix()), // same end time as for primary network + pendingDSValidatorID, + testSubnet1.ID(), + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err != nil { + t.Fatal(err) + } + } + + // Case: Proposed validator start validating at/before current timestamp + // First, advance the timestamp + newTimestamp := defaultGenesisTime.Add(2 * time.Second) + env.state.SetTimestamp(newTimestamp) + + { + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, // weight + uint64(newTimestamp.Unix()), // start time + uint64(newTimestamp.Add(defaultMinStakingDuration).Unix()), // end time + ids.NodeID(nodeID), // node ID + testSubnet1.ID(), // subnet ID + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed verification because starts validating at current timestamp") + } + } + + // reset the timestamp + env.state.SetTimestamp(defaultGenesisTime) + + // Case: Proposed validator already validating the subnet + // First, add validator as validator of subnet + subnetTx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, // weight + uint64(defaultValidateStartTime.Unix()), // start time + uint64(defaultValidateEndTime.Unix()), // end time + ids.NodeID(nodeID), // node ID + testSubnet1.ID(), // subnet ID + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, + ) + if err != nil { + t.Fatal(err) + } + + staker = state.NewCurrentStaker( + subnetTx.ID(), + subnetTx.Unsigned.(*txs.AddSubnetValidatorTx), + 0, + ) + + env.state.PutCurrentValidator(staker) + env.state.AddTx(subnetTx, status.Committed) + env.state.SetHeight(dummyHeight) + if err := env.state.Commit(); err != nil { + t.Fatal(err) + } + + { + // Node with ID nodeIDKey.PublicKey().Address() now validating subnet with ID testSubnet1.ID + duplicateSubnetTx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, // weight + uint64(defaultValidateStartTime.Unix()), // start time + uint64(defaultValidateEndTime.Unix()), // end time + ids.NodeID(nodeID), // node ID + testSubnet1.ID(), // subnet ID + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: duplicateSubnetTx, + } + err = duplicateSubnetTx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed verification because validator already validating the specified subnet") + } + } + + env.state.DeleteCurrentValidator(staker) + env.state.SetHeight(dummyHeight) + if err := env.state.Commit(); err != nil { + t.Fatal(err) + } + + { + // Case: Too many signatures + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, // weight + uint64(defaultGenesisTime.Unix()), // start time + uint64(defaultGenesisTime.Add(defaultMinStakingDuration).Unix())+1, // end time + ids.NodeID(nodeID), // node ID + testSubnet1.ID(), // subnet ID + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1], testSubnet1ControlKeys[2]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed verification because tx has 3 signatures but only 2 needed") + } + } + + { + // Case: Too few signatures + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, // weight + uint64(defaultGenesisTime.Unix()), // start time + uint64(defaultGenesisTime.Add(defaultMinStakingDuration).Unix()), // end time + ids.NodeID(nodeID), // node ID + testSubnet1.ID(), // subnet ID + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[2]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + // Remove a signature + addSubnetValidatorTx := tx.Unsigned.(*txs.AddSubnetValidatorTx) + input := addSubnetValidatorTx.SubnetAuth.(*secp256k1fx.Input) + input.SigIndices = input.SigIndices[1:] + // This tx was syntactically verified when it was created...pretend it wasn't so we don't use cache + addSubnetValidatorTx.SyntacticallyVerified = false + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed verification because not enough control sigs") + } + } + + { + // Case: Control Signature from invalid key (keys[3] is not a control key) + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, // weight + uint64(defaultGenesisTime.Unix()), // start time + uint64(defaultGenesisTime.Add(defaultMinStakingDuration).Unix()), // end time + ids.NodeID(nodeID), // node ID + testSubnet1.ID(), // subnet ID + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], preFundedKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + // Replace a valid signature with one from keys[3] + sig, err := preFundedKeys[3].SignHash(hashing.ComputeHash256(tx.Unsigned.Bytes())) + if err != nil { + t.Fatal(err) + } + copy(tx.Creds[0].(*secp256k1fx.Credential).Sigs[0][:], sig) + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed verification because a control sig is invalid") + } + } + + { + // Case: Proposed validator in pending validator set for subnet + // First, add validator to pending validator set of subnet + tx, err := env.txBuilder.NewAddSubnetValidatorTx( + defaultWeight, // weight + uint64(defaultGenesisTime.Unix())+1, // start time + uint64(defaultGenesisTime.Add(defaultMinStakingDuration).Unix())+1, // end time + ids.NodeID(nodeID), // node ID + testSubnet1.ID(), // subnet ID + []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + staker = state.NewCurrentStaker( + subnetTx.ID(), + subnetTx.Unsigned.(*txs.AddSubnetValidatorTx), + 0, + ) + + env.state.PutCurrentValidator(staker) + env.state.AddTx(tx, status.Committed) + env.state.SetHeight(dummyHeight) + if err := env.state.Commit(); err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed verification because validator already in pending validator set of the specified subnet") + } + } +} + +func TestStandardTxExecutorAddValidator(t *testing.T) { + env := newEnvironment() + env.ctx.Lock.Lock() + defer func() { + if err := shutdownEnvironment(env); err != nil { + t.Fatal(err) + } + }() + + nodeID := ids.GenerateTestNodeID() + + env.config.BanffTime = env.state.GetTimestamp() + + { + // Case: Validator's start time too early + tx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, + uint64(defaultValidateStartTime.Unix())-1, + uint64(defaultValidateEndTime.Unix()), + nodeID, + ids.ShortEmpty, + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should've errored because start time too early") + } + } + + { + // Case: Validator's start time too far in the future + tx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, + uint64(defaultValidateStartTime.Add(MaxFutureStartTime).Unix()+1), + uint64(defaultValidateStartTime.Add(MaxFutureStartTime).Add(defaultMinStakingDuration).Unix()+1), + nodeID, + ids.ShortEmpty, + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should've errored because start time too far in the future") + } + } + + { + // Case: Validator already validating primary network + tx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, + uint64(defaultValidateStartTime.Unix()), + uint64(defaultValidateEndTime.Unix()), + nodeID, + ids.ShortEmpty, + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should've errored because validator already validating") + } + } + + { + // Case: Validator in pending validator set of primary network + startTime := defaultGenesisTime.Add(1 * time.Second) + tx, err := env.txBuilder.NewAddValidatorTx( + env.config.MinValidatorStake, // stake amount + uint64(startTime.Unix()), // start time + uint64(startTime.Add(defaultMinStakingDuration).Unix()), // end time + nodeID, + ids.ShortEmpty, + reward.PercentDenominator, // shares + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, // change addr // key + ) + if err != nil { + t.Fatal(err) + } + + staker := state.NewCurrentStaker( + tx.ID(), + tx.Unsigned.(*txs.AddValidatorTx), + 0, + ) + + env.state.PutCurrentValidator(staker) + env.state.AddTx(tx, status.Committed) + dummyHeight := uint64(1) + env.state.SetHeight(dummyHeight) + if err := env.state.Commit(); err != nil { + t.Fatal(err) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed because validator in pending validator set") + } + } + + { + // Case: Validator doesn't have enough tokens to cover stake amount + tx, err := env.txBuilder.NewAddValidatorTx( // create the tx + env.config.MinValidatorStake, + uint64(defaultValidateStartTime.Unix()), + uint64(defaultValidateEndTime.Unix()), + nodeID, + ids.ShortEmpty, + reward.PercentDenominator, + []*crypto.PrivateKeySECP256K1R{preFundedKeys[0]}, + ids.ShortEmpty, // change addr + ) + if err != nil { + t.Fatal(err) + } + + // Remove all UTXOs owned by preFundedKeys[0] + utxoIDs, err := env.state.UTXOIDs(preFundedKeys[0].PublicKey().Address().Bytes(), ids.Empty, math.MaxInt32) + if err != nil { + t.Fatal(err) + } + for _, utxoID := range utxoIDs { + env.state.DeleteUTXO(utxoID) + } + + onAcceptState, err := state.NewDiff(lastAcceptedID, env) + if err != nil { + t.Fatal(err) + } + + executor := StandardTxExecutor{ + Backend: &env.backend, + State: onAcceptState, + Tx: tx, + } + err = tx.Unsigned.Visit(&executor) + if err == nil { + t.Fatal("should have failed because tx fee paying key has no funds") + } + } +} + +// Returns a RemoveSubnetValidatorTx that passes syntactic verification. +func newRemoveSubnetValidatorTx(t *testing.T) (*txs.RemoveSubnetValidatorTx, *txs.Tx) { + t.Helper() + + creds := []verify.Verifiable{ + &secp256k1fx.Credential{ + Sigs: make([][65]byte, 1), + }, + &secp256k1fx.Credential{ + Sigs: make([][65]byte, 1), + }, + } + unsignedTx := &txs.RemoveSubnetValidatorTx{ + BaseTx: txs.BaseTx{ + BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{{ + UTXOID: avax.UTXOID{ + TxID: ids.GenerateTestID(), + }, + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + In: &secp256k1fx.TransferInput{ + Amt: 1, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0, 1}, + }, + }, + }}, + Outs: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + }, + }, + }, + }, + Memo: []byte("hi"), + }, + }, + Subnet: ids.GenerateTestID(), + NodeID: ids.GenerateTestNodeID(), + SubnetAuth: &secp256k1fx.Credential{ + Sigs: make([][65]byte, 1), + }, + } + tx := &txs.Tx{ + Unsigned: unsignedTx, + Creds: creds, + } + if err := tx.Sign(txs.Codec, nil); err != nil { + t.Fatal(err) + } + return unsignedTx, tx +} + +// mock implementations that can be used in tests +// for verifying RemoveSubnetValidatorTx. +type removeSubnetValidatorTxVerifyEnv struct { + banffTime time.Time + fx *fx.MockFx + flowChecker *utxo.MockVerifier + unsignedTx *txs.RemoveSubnetValidatorTx + tx *txs.Tx + state *state.MockDiff + staker *state.Staker +} + +// Returns mock implementations that can be used in tests +// for verifying RemoveSubnetValidatorTx. +func newValidRemoveSubnetValidatorTxVerifyEnv(t *testing.T, ctrl *gomock.Controller) removeSubnetValidatorTxVerifyEnv { + t.Helper() + + now := time.Now() + mockFx := fx.NewMockFx(ctrl) + mockFlowChecker := utxo.NewMockVerifier(ctrl) + unsignedTx, tx := newRemoveSubnetValidatorTx(t) + mockState := state.NewMockDiff(ctrl) + return removeSubnetValidatorTxVerifyEnv{ + banffTime: now, + fx: mockFx, + flowChecker: mockFlowChecker, + unsignedTx: unsignedTx, + tx: tx, + state: mockState, + staker: &state.Staker{ + TxID: ids.GenerateTestID(), + NodeID: ids.GenerateTestNodeID(), + Priority: txs.SubnetPermissionedValidatorCurrentPriority, + }, + } +} + +func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { + type test struct { + name string + newExecutor func(*gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) + shouldErr bool + expectedErr error + } + + tests := []test{ + { + name: "valid tx", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + + // Set dependency expectations. + env.state.EXPECT().GetTimestamp().Return(env.banffTime.Add(time.Second)).Times(1) + env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil).Times(1) + subnetOwner := fx.NewMockOwner(ctrl) + subnetTx := &txs.Tx{ + Unsigned: &txs.CreateSubnetTx{ + Owner: subnetOwner, + }, + } + env.state.EXPECT().GetTx(env.unsignedTx.Subnet).Return(subnetTx, status.Committed, nil).Times(1) + env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1) + env.flowChecker.EXPECT().VerifySpend( + env.unsignedTx, env.state, env.unsignedTx.Ins, env.unsignedTx.Outs, env.tx.Creds[:len(env.tx.Creds)-1], gomock.Any(), + ).Return(nil).Times(1) + env.state.EXPECT().DeleteCurrentValidator(env.staker) + env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins)) + env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs)) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: false, + }, + { + name: "not yet banff time", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime.Add(-1 * time.Second)).Times(1) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Ctx: &snow.Context{}, + }, + State: env.state, + Tx: env.tx, + } + return env.unsignedTx, e + }, + shouldErr: true, + expectedErr: errRemoveSubnetValidatorTxBeforeBanff, + }, + { + name: "tx fails syntactic verification", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + // Setting the subnet ID to the Primary Network ID makes the tx fail syntactic verification + env.tx.Unsigned.(*txs.RemoveSubnetValidatorTx).Subnet = constants.PrimaryNetworkID + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: true, + }, + { + name: "node isn't a validator of the subnet", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound) + env.state.EXPECT().GetPendingValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: true, + expectedErr: errNotValidator, + }, + { + name: "validator is permissionless", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + + staker := *env.staker + staker.Priority = txs.SubnetPermissionlessValidatorCurrentPriority + + // Set dependency expectations. + env.state.EXPECT().GetTimestamp().Return(env.banffTime.Add(time.Second)).Times(1) + env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(&staker, nil).Times(1) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: true, + expectedErr: errRemovePermissionlessValidator, + }, + { + name: "tx has no credentials", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + // Remove credentials + env.tx.Creds = nil + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: true, + expectedErr: errWrongNumberOfCredentials, + }, + { + name: "can't find subnet", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) + env.state.EXPECT().GetTx(env.unsignedTx.Subnet).Return(nil, status.Unknown, database.ErrNotFound) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: true, + expectedErr: errCantFindSubnet, + }, + { + name: "no permission to remove validator", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) + subnetOwner := fx.NewMockOwner(ctrl) + subnetTx := &txs.Tx{ + Unsigned: &txs.CreateSubnetTx{ + Owner: subnetOwner, + }, + } + env.state.EXPECT().GetTx(env.unsignedTx.Subnet).Return(subnetTx, status.Committed, nil) + env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(errors.New("")) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: true, + expectedErr: errUnauthorizedSubnetModification, + }, + { + name: "flow checker failed", + newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { + env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) + subnetOwner := fx.NewMockOwner(ctrl) + subnetTx := &txs.Tx{ + Unsigned: &txs.CreateSubnetTx{ + Owner: subnetOwner, + }, + } + env.state.EXPECT().GetTx(env.unsignedTx.Subnet).Return(subnetTx, status.Committed, nil) + env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil) + env.flowChecker.EXPECT().VerifySpend( + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(errors.New("")) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + shouldErr: true, + expectedErr: errFlowCheckFailed, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + unsignedTx, executor := tt.newExecutor(ctrl) + err := executor.RemoveSubnetValidatorTx(unsignedTx) + if tt.shouldErr { + require.Error(err) + if tt.expectedErr != nil { + require.ErrorIs(err, tt.expectedErr) + } + return + } + require.NoError(err) + }) + } +} + +// Returns a TransformSubnetTx that passes syntactic verification. +func newTransformSubnetTx(t *testing.T) (*txs.TransformSubnetTx, *txs.Tx) { + t.Helper() + + creds := []verify.Verifiable{ + &secp256k1fx.Credential{ + Sigs: make([][65]byte, 1), + }, + &secp256k1fx.Credential{ + Sigs: make([][65]byte, 1), + }, + } + unsignedTx := &txs.TransformSubnetTx{ + BaseTx: txs.BaseTx{ + BaseTx: avax.BaseTx{ + Ins: []*avax.TransferableInput{{ + UTXOID: avax.UTXOID{ + TxID: ids.GenerateTestID(), + }, + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + In: &secp256k1fx.TransferInput{ + Amt: 1, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0, 1}, + }, + }, + }}, + Outs: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 1, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ids.GenerateTestShortID()}, + }, + }, + }, + }, + Memo: []byte("hi"), + }, + }, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator, + MinDelegatorStake: 1, + MaxValidatorWeightFactor: 1, + UptimeRequirement: reward.PercentDenominator, + SubnetAuth: &secp256k1fx.Credential{ + Sigs: make([][65]byte, 1), + }, + } + tx := &txs.Tx{ + Unsigned: unsignedTx, + Creds: creds, + } + if err := tx.Sign(txs.Codec, nil); err != nil { + t.Fatal(err) + } + return unsignedTx, tx +} + +// mock implementations that can be used in tests +// for verifying TransformSubnetTx. +type transformSubnetTxVerifyEnv struct { + banffTime time.Time + fx *fx.MockFx + flowChecker *utxo.MockVerifier + unsignedTx *txs.TransformSubnetTx + tx *txs.Tx + state *state.MockDiff + staker *state.Staker +} + +// Returns mock implementations that can be used in tests +// for verifying TransformSubnetTx. +func newValidTransformSubnetTxVerifyEnv(t *testing.T, ctrl *gomock.Controller) transformSubnetTxVerifyEnv { + t.Helper() + + now := time.Now() + mockFx := fx.NewMockFx(ctrl) + mockFlowChecker := utxo.NewMockVerifier(ctrl) + unsignedTx, tx := newTransformSubnetTx(t) + mockState := state.NewMockDiff(ctrl) + return transformSubnetTxVerifyEnv{ + banffTime: now, + fx: mockFx, + flowChecker: mockFlowChecker, + unsignedTx: unsignedTx, + tx: tx, + state: mockState, + staker: &state.Staker{ + TxID: ids.GenerateTestID(), + NodeID: ids.GenerateTestNodeID(), + }, + } +} + +func TestStandardExecutorTransformSubnetTx(t *testing.T) { + type test struct { + name string + newExecutor func(*gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) + err error + } + + tests := []test{ + { + name: "not yet banff time", + newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { + env := newValidTransformSubnetTxVerifyEnv(t, ctrl) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime.Add(-1 * time.Second)).Times(1) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Ctx: &snow.Context{}, + }, + State: env.state, + Tx: env.tx, + } + return env.unsignedTx, e + }, + err: errTransformSubnetTxBeforeBanff, + }, + { + name: "tx fails syntactic verification", + newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { + env := newValidTransformSubnetTxVerifyEnv(t, ctrl) + // Setting the tx to nil makes the tx fail syntactic verification + env.tx.Unsigned = (*txs.TransformSubnetTx)(nil) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + err: txs.ErrNilTx, + }, + { + name: "max stake duration too large", + newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { + env := newValidTransformSubnetTxVerifyEnv(t, ctrl) + env.unsignedTx.MaxStakeDuration = math.MaxUint32 + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + err: errMaxStakeDurationTooLarge, + }, + { + name: "fail subnet authorization", + newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { + env := newValidTransformSubnetTxVerifyEnv(t, ctrl) + // Remove credentials + env.tx.Creds = nil + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + MaxStakeDuration: math.MaxInt64, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + err: errWrongNumberOfCredentials, + }, + { + name: "flow checker failed", + newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { + env := newValidTransformSubnetTxVerifyEnv(t, ctrl) + env.state = state.NewMockDiff(ctrl) + env.state.EXPECT().GetTimestamp().Return(env.banffTime).Times(1) + subnetOwner := fx.NewMockOwner(ctrl) + subnetTx := &txs.Tx{ + Unsigned: &txs.CreateSubnetTx{ + Owner: subnetOwner, + }, + } + env.state.EXPECT().GetTx(env.unsignedTx.Subnet).Return(subnetTx, status.Committed, nil) + env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1) + env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil) + env.flowChecker.EXPECT().VerifySpend( + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(errFlowCheckFailed) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + MaxStakeDuration: math.MaxInt64, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + err: errFlowCheckFailed, + }, + { + name: "valid tx", + newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { + env := newValidTransformSubnetTxVerifyEnv(t, ctrl) + + // Set dependency expectations. + env.state.EXPECT().GetTimestamp().Return(env.banffTime.Add(time.Second)).Times(1) + subnetOwner := fx.NewMockOwner(ctrl) + subnetTx := &txs.Tx{ + Unsigned: &txs.CreateSubnetTx{ + Owner: subnetOwner, + }, + } + env.state.EXPECT().GetTx(env.unsignedTx.Subnet).Return(subnetTx, status.Committed, nil).Times(1) + env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1) + env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1) + env.flowChecker.EXPECT().VerifySpend( + env.unsignedTx, env.state, env.unsignedTx.Ins, env.unsignedTx.Outs, env.tx.Creds[:len(env.tx.Creds)-1], gomock.Any(), + ).Return(nil).Times(1) + env.state.EXPECT().AddSubnetTransformation(env.tx) + env.state.EXPECT().SetCurrentSupply(env.unsignedTx.Subnet, env.unsignedTx.InitialSupply) + env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins)) + env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs)) + e := &StandardTxExecutor{ + Backend: &Backend{ + Config: &config.Config{ + BanffTime: env.banffTime, + MaxStakeDuration: math.MaxInt64, + }, + Bootstrapped: &utils.AtomicBool{}, + Fx: env.fx, + FlowChecker: env.flowChecker, + Ctx: &snow.Context{}, + }, + Tx: env.tx, + State: env.state, + } + e.Bootstrapped.SetValue(true) + return env.unsignedTx, e + }, + err: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + unsignedTx, executor := tt.newExecutor(ctrl) + err := executor.TransformSubnetTx(unsignedTx) + require.ErrorIs(err, tt.err) + }) + } +} diff --git a/avalanchego/vms/platformvm/txs/executor/state_changes.go b/avalanchego/vms/platformvm/txs/executor/state_changes.go new file mode 100644 index 00000000..d45f1938 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/executor/state_changes.go @@ -0,0 +1,231 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "errors" + "fmt" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +var ( + errChildBlockAfterStakerChangeTime = errors.New("proposed timestamp later than next staker change time") + errChildBlockBeyondSyncBound = errors.New("proposed timestamp is too far in the future relative to local time") +) + +// VerifyNewChainTime returns nil if the [newChainTime] is a valid chain time +// given the wall clock time ([now]) and when the next staking set change occurs +// ([nextStakerChangeTime]). +// Requires: +// - [newChainTime] <= [nextStakerChangeTime]: so that no staking set changes +// are skipped. +// - [newChainTime] <= [now] + [SyncBound]: to ensure chain time approximates +// "real" time. +func VerifyNewChainTime( + newChainTime, + nextStakerChangeTime, + now time.Time, +) error { + // Only allow timestamp to move as far forward as the time of the next + // staker set change + if newChainTime.After(nextStakerChangeTime) { + return fmt.Errorf( + "%w, proposed timestamp (%s), next staker change time (%s)", + errChildBlockAfterStakerChangeTime, + newChainTime, + nextStakerChangeTime, + ) + } + + // Only allow timestamp to reasonably far forward + maxNewChainTime := now.Add(SyncBound) + if newChainTime.After(maxNewChainTime) { + return fmt.Errorf( + "%w, proposed time (%s), local time (%s)", + errChildBlockBeyondSyncBound, + newChainTime, + now, + ) + } + return nil +} + +type StateChanges interface { + Apply(onAccept state.Diff) + Len() int +} + +type stateChanges struct { + updatedSupplies map[ids.ID]uint64 + currentValidatorsToAdd []*state.Staker + currentDelegatorsToAdd []*state.Staker + pendingValidatorsToRemove []*state.Staker + pendingDelegatorsToRemove []*state.Staker + currentValidatorsToRemove []*state.Staker +} + +func (s *stateChanges) Apply(stateDiff state.Diff) { + for subnetID, supply := range s.updatedSupplies { + stateDiff.SetCurrentSupply(subnetID, supply) + } + + for _, currentValidatorToAdd := range s.currentValidatorsToAdd { + stateDiff.PutCurrentValidator(currentValidatorToAdd) + } + for _, pendingValidatorToRemove := range s.pendingValidatorsToRemove { + stateDiff.DeletePendingValidator(pendingValidatorToRemove) + } + for _, currentDelegatorToAdd := range s.currentDelegatorsToAdd { + stateDiff.PutCurrentDelegator(currentDelegatorToAdd) + } + for _, pendingDelegatorToRemove := range s.pendingDelegatorsToRemove { + stateDiff.DeletePendingDelegator(pendingDelegatorToRemove) + } + for _, currentValidatorToRemove := range s.currentValidatorsToRemove { + stateDiff.DeleteCurrentValidator(currentValidatorToRemove) + } +} + +func (s *stateChanges) Len() int { + return len(s.currentValidatorsToAdd) + len(s.currentDelegatorsToAdd) + + len(s.pendingValidatorsToRemove) + len(s.pendingDelegatorsToRemove) + + len(s.currentValidatorsToRemove) +} + +// AdvanceTimeTo does not modify [parentState]. +// Instead it returns all the StateChanges caused by advancing the chain time to +// the [newChainTime]. +func AdvanceTimeTo( + backend *Backend, + parentState state.Chain, + newChainTime time.Time, +) (StateChanges, error) { + pendingStakerIterator, err := parentState.GetPendingStakerIterator() + if err != nil { + return nil, err + } + defer pendingStakerIterator.Release() + + changes := &stateChanges{ + updatedSupplies: make(map[ids.ID]uint64), + } + + // Add to the staker set any pending stakers whose start time is at or + // before the new timestamp + for pendingStakerIterator.Next() { + stakerToRemove := pendingStakerIterator.Value() + if stakerToRemove.StartTime.After(newChainTime) { + break + } + + stakerToAdd := *stakerToRemove + stakerToAdd.NextTime = stakerToRemove.EndTime + stakerToAdd.Priority = txs.PendingToCurrentPriorities[stakerToRemove.Priority] + + if stakerToRemove.Priority == txs.SubnetPermissionedValidatorPendingPriority { + // Invariant: [txTimestamp] <= [nextStakerChangeTime]. + // Invariant: minimum stake duration is > 0. + // + // Both of the above invariants ensure the staker we are adding here + // should never be attempted to be removed in the following loop. + + changes.currentValidatorsToAdd = append(changes.currentValidatorsToAdd, &stakerToAdd) + changes.pendingValidatorsToRemove = append(changes.pendingValidatorsToRemove, stakerToRemove) + continue + } + + supply, ok := changes.updatedSupplies[stakerToRemove.SubnetID] + if !ok { + supply, err = parentState.GetCurrentSupply(stakerToRemove.SubnetID) + if err != nil { + return nil, err + } + } + + rewards, err := GetRewardsCalculator(backend, parentState, stakerToRemove.SubnetID) + if err != nil { + return nil, err + } + + potentialReward := rewards.Calculate( + stakerToRemove.EndTime.Sub(stakerToRemove.StartTime), + stakerToRemove.Weight, + supply, + ) + stakerToAdd.PotentialReward = potentialReward + + // Invariant: [rewards.Calculate] can never return a [potentialReward] + // such that [supply + potentialReward > maximumSupply]. + changes.updatedSupplies[stakerToRemove.SubnetID] = supply + potentialReward + + switch stakerToRemove.Priority { + case txs.PrimaryNetworkValidatorPendingPriority, txs.SubnetPermissionlessValidatorPendingPriority: + changes.currentValidatorsToAdd = append(changes.currentValidatorsToAdd, &stakerToAdd) + changes.pendingValidatorsToRemove = append(changes.pendingValidatorsToRemove, stakerToRemove) + + case txs.PrimaryNetworkDelegatorApricotPendingPriority, txs.PrimaryNetworkDelegatorBanffPendingPriority, txs.SubnetPermissionlessDelegatorPendingPriority: + changes.currentDelegatorsToAdd = append(changes.currentDelegatorsToAdd, &stakerToAdd) + changes.pendingDelegatorsToRemove = append(changes.pendingDelegatorsToRemove, stakerToRemove) + + default: + return nil, fmt.Errorf("expected staker priority got %d", stakerToRemove.Priority) + } + } + + currentStakerIterator, err := parentState.GetCurrentStakerIterator() + if err != nil { + return nil, err + } + defer currentStakerIterator.Release() + + for currentStakerIterator.Next() { + stakerToRemove := currentStakerIterator.Value() + if stakerToRemove.EndTime.After(newChainTime) { + break + } + + // Invariant: Permissioned stakers are encountered first for a given + // timestamp because their priority is the smallest. + if stakerToRemove.Priority != txs.SubnetPermissionedValidatorCurrentPriority { + // Permissionless stakers are removed by the RewardValidatorTx, not + // an AdvanceTimeTx. + break + } + + changes.currentValidatorsToRemove = append(changes.currentValidatorsToRemove, stakerToRemove) + } + return changes, nil +} + +func GetRewardsCalculator( + backend *Backend, + parentState state.Chain, + subnetID ids.ID, +) (reward.Calculator, error) { + if subnetID == constants.PrimaryNetworkID { + return backend.Rewards, nil + } + + transformSubnetIntf, err := parentState.GetSubnetTransformation(subnetID) + if err != nil { + return nil, err + } + transformSubnet, ok := transformSubnetIntf.Unsigned.(*txs.TransformSubnetTx) + if !ok { + return nil, errIsNotTransformSubnetTx + } + + return reward.NewCalculator(reward.Config{ + MaxConsumptionRate: transformSubnet.MaxConsumptionRate, + MinConsumptionRate: transformSubnet.MinConsumptionRate, + MintingPeriod: backend.Config.RewardConfig.MintingPeriod, + SupplyCap: transformSubnet.MaximumSupply, + }), nil +} diff --git a/avalanchego/vms/platformvm/txs/executor/subnet_tx_verification.go b/avalanchego/vms/platformvm/txs/executor/subnet_tx_verification.go new file mode 100644 index 00000000..276481be --- /dev/null +++ b/avalanchego/vms/platformvm/txs/executor/subnet_tx_verification.go @@ -0,0 +1,90 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package executor + +import ( + "errors" + "fmt" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +var ( + errWrongNumberOfCredentials = errors.New("should have the same number of credentials as inputs") + errCantFindSubnet = errors.New("couldn't find subnet") + errIsNotSubnet = errors.New("is not a subnet") + errIsImmutable = errors.New("is immutable") + errUnauthorizedSubnetModification = errors.New("unauthorized subnet modification") +) + +// verifyPoASubnetAuthorization carries out the validation for modifying a PoA +// subnet. This is an extension of [verifySubnetAuthorization] that additionally +// verifies that the subnet being modified is currently a PoA subnet. +func verifyPoASubnetAuthorization( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + subnetID ids.ID, + subnetAuth verify.Verifiable, +) ([]verify.Verifiable, error) { + creds, err := verifySubnetAuthorization(backend, chainState, sTx, subnetID, subnetAuth) + if err != nil { + return nil, err + } + + _, err = chainState.GetSubnetTransformation(subnetID) + if err == nil { + return nil, fmt.Errorf("%q %w", subnetID, errIsImmutable) + } + if err != database.ErrNotFound { + return nil, err + } + + return creds, nil +} + +// verifySubnetAuthorization carries out the validation for modifying a subnet. +// The last credential in [sTx.Creds] is used as the subnet authorization. +// Returns the remaining tx credentials that should be used to authorize the +// other operations in the tx. +func verifySubnetAuthorization( + backend *Backend, + chainState state.Chain, + sTx *txs.Tx, + subnetID ids.ID, + subnetAuth verify.Verifiable, +) ([]verify.Verifiable, error) { + if len(sTx.Creds) == 0 { + // Ensure there is at least one credential for the subnet authorization + return nil, errWrongNumberOfCredentials + } + + baseTxCredsLen := len(sTx.Creds) - 1 + subnetCred := sTx.Creds[baseTxCredsLen] + + subnetIntf, _, err := chainState.GetTx(subnetID) + if err != nil { + return nil, fmt.Errorf( + "%w %q: %s", + errCantFindSubnet, + subnetID, + err, + ) + } + + subnet, ok := subnetIntf.Unsigned.(*txs.CreateSubnetTx) + if !ok { + return nil, fmt.Errorf("%q %w", subnetID, errIsNotSubnet) + } + + if err := backend.Fx.VerifyPermission(sTx.Unsigned, subnetAuth, subnetCred, subnet.Owner); err != nil { + return nil, fmt.Errorf("%w: %s", errUnauthorizedSubnetModification, err) + } + + return sTx.Creds[:baseTxCredsLen], nil +} diff --git a/avalanchego/vms/platformvm/txs/executor/tx_mempool_verifier.go b/avalanchego/vms/platformvm/txs/executor/tx_mempool_verifier.go index 4e49e312..31ca6c36 100644 --- a/avalanchego/vms/platformvm/txs/executor/tx_mempool_verifier.go +++ b/avalanchego/vms/platformvm/txs/executor/tx_mempool_verifier.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package executor @@ -51,6 +51,23 @@ func (v *MempoolTxVerifier) ExportTx(tx *txs.ExportTx) error { return v.standardTx(tx) } +func (v *MempoolTxVerifier) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { + return v.standardTx(tx) +} + +func (v *MempoolTxVerifier) TransformSubnetTx(tx *txs.TransformSubnetTx) error { + return v.standardTx(tx) +} + +func (v *MempoolTxVerifier) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { + return v.standardTx(tx) +} + +func (v *MempoolTxVerifier) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { + return v.standardTx(tx) +} + +// TODO: simplify this function after Banff is activated. func (v *MempoolTxVerifier) proposalTx(tx txs.StakerTx) error { startTime := tx.StartTime() maxLocalStartTime := v.Clk.Time().Add(MaxFutureStartTime) @@ -58,15 +75,31 @@ func (v *MempoolTxVerifier) proposalTx(tx txs.StakerTx) error { return errFutureStakeTime } + onCommitState, err := state.NewDiff(v.ParentID, v.StateVersions) + if err != nil { + return err + } + + // Make sure that the Banff fork check will pass. + currentChainTime := onCommitState.GetTimestamp() + if v.Backend.Config.IsBanffActivated(currentChainTime) { + return v.standardTx(tx) + } + + onAbortState, err := state.NewDiff(v.ParentID, v.StateVersions) + if err != nil { + return err + } + executor := ProposalTxExecutor{ + OnCommitState: onCommitState, + OnAbortState: onAbortState, Backend: v.Backend, - ParentID: v.ParentID, - StateVersions: v.StateVersions, Tx: v.Tx, } - err := tx.Visit(&executor) - // We ignore [errFutureStakeTime] here because an advanceTimeTx will be - // issued before this transaction is issued. + err = tx.Visit(&executor) + // We ignore [errFutureStakeTime] here because the time will be advanced + // when this transaction is issued. if errors.Is(err, errFutureStakeTime) { return nil } @@ -87,5 +120,11 @@ func (v *MempoolTxVerifier) standardTx(tx txs.UnsignedTx) error { State: state, Tx: v.Tx, } - return tx.Visit(&executor) + err = tx.Visit(&executor) + // We ignore [errFutureStakeTime] here because the time will be advanced + // when this transaction is issued. + if errors.Is(err, errFutureStakeTime) { + return nil + } + return err } diff --git a/avalanchego/vms/platformvm/txs/export_tx.go b/avalanchego/vms/platformvm/txs/export_tx.go index 2f1af7f1..e05177ac 100644 --- a/avalanchego/vms/platformvm/txs/export_tx.go +++ b/avalanchego/vms/platformvm/txs/export_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -15,8 +15,7 @@ import ( ) var ( - _ UnsignedTx = &ExportTx{} - _ secp256k1fx.UnsignedTx = &ExportTx{} + _ UnsignedTx = &ExportTx{} ErrWrongLocktime = errors.New("wrong locktime reported") errNoExportOutputs = errors.New("no export outputs") diff --git a/avalanchego/vms/platformvm/txs/import_tx.go b/avalanchego/vms/platformvm/txs/import_tx.go index bf856069..45cff351 100644 --- a/avalanchego/vms/platformvm/txs/import_tx.go +++ b/avalanchego/vms/platformvm/txs/import_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -14,8 +14,7 @@ import ( ) var ( - _ UnsignedTx = &ImportTx{} - _ secp256k1fx.UnsignedTx = &ImportTx{} + _ UnsignedTx = &ImportTx{} errNoImportInputs = errors.New("tx has no imported inputs") ) diff --git a/avalanchego/vms/platformvm/txs/mempool/issuer.go b/avalanchego/vms/platformvm/txs/mempool/issuer.go new file mode 100644 index 00000000..0a7d8177 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/mempool/issuer.go @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package mempool + +import ( + "errors" + + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +var ( + _ txs.Visitor = &issuer{} + + errCantIssueAdvanceTimeTx = errors.New("can not issue an advance time tx") + errCantIssueRewardValidatorTx = errors.New("can not issue a reward validator tx") +) + +type issuer struct { + m *mempool + tx *txs.Tx +} + +func (i *issuer) AdvanceTimeTx(tx *txs.AdvanceTimeTx) error { + return errCantIssueAdvanceTimeTx +} + +func (i *issuer) RewardValidatorTx(tx *txs.RewardValidatorTx) error { + return errCantIssueRewardValidatorTx +} + +func (i *issuer) AddValidatorTx(*txs.AddValidatorTx) error { + i.m.addStakerTx(i.tx) + return nil +} + +func (i *issuer) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error { + i.m.addStakerTx(i.tx) + return nil +} + +func (i *issuer) AddDelegatorTx(tx *txs.AddDelegatorTx) error { + i.m.addStakerTx(i.tx) + return nil +} + +func (i *issuer) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { + i.m.addDecisionTx(i.tx) + return nil +} + +func (i *issuer) CreateChainTx(tx *txs.CreateChainTx) error { + i.m.addDecisionTx(i.tx) + return nil +} + +func (i *issuer) CreateSubnetTx(tx *txs.CreateSubnetTx) error { + i.m.addDecisionTx(i.tx) + return nil +} + +func (i *issuer) ImportTx(tx *txs.ImportTx) error { + i.m.addDecisionTx(i.tx) + return nil +} + +func (i *issuer) ExportTx(tx *txs.ExportTx) error { + i.m.addDecisionTx(i.tx) + return nil +} + +func (i *issuer) TransformSubnetTx(tx *txs.TransformSubnetTx) error { + i.m.addDecisionTx(i.tx) + return nil +} + +func (i *issuer) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { + i.m.addStakerTx(i.tx) + return nil +} + +func (i *issuer) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { + i.m.addStakerTx(i.tx) + return nil +} diff --git a/avalanchego/vms/platformvm/txs/mempool/mempool.go b/avalanchego/vms/platformvm/txs/mempool/mempool.go index f1250a86..c7787af0 100644 --- a/avalanchego/vms/platformvm/txs/mempool/mempool.go +++ b/avalanchego/vms/platformvm/txs/mempool/mempool.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package mempool @@ -31,12 +31,9 @@ const ( ) var ( - _ Mempool = &mempool{} - _ txs.Visitor = &mempoolIssuer{} + _ Mempool = &mempool{} - errMempoolFull = errors.New("mempool is full") - errCantIssueAdvanceTimeTx = errors.New("can not issue an advance time tx") - errCantIssueRewardValidatorTx = errors.New("can not issue a reward validator tx") + errMempoolFull = errors.New("mempool is full") ) type BlockTimer interface { @@ -55,25 +52,39 @@ type Mempool interface { Add(tx *txs.Tx) error Has(txID ids.ID) bool Get(txID ids.ID) *txs.Tx - - AddDecisionTx(tx *txs.Tx) - AddProposalTx(tx *txs.Tx) - - HasDecisionTxs() bool - HasProposalTx() bool - - RemoveDecisionTxs(txs []*txs.Tx) - RemoveProposalTx(tx *txs.Tx) - - PopDecisionTxs(maxTxsBytes int) []*txs.Tx - PopProposalTx() *txs.Tx + Remove(txs []*txs.Tx) + + // Following Banff activation, all mempool transactions, + // (both decision and staker) are included into Standard blocks. + // HasTxs allow to check for availability of any mempool transaction. + HasTxs() bool + // PeekTxs returns the next txs for Banff blocks + // up to maxTxsBytes without removing them from the mempool. + // It returns nil if !HasTxs() + PeekTxs(maxTxsBytes int) []*txs.Tx + + HasStakerTx() bool + // PeekStakerTx returns the next stakerTx without removing it from mempool. + // It returns nil if !HasStakerTx(). + // It's guaranteed that the returned tx, if not nil, is a StakerTx. + PeekStakerTx() *txs.Tx // Note: dropped txs are added to droppedTxIDs but not - // not evicted from unissued decision/proposal txs. + // not evicted from unissued decision/staker txs. // This allows previously dropped txs to be possibly // reissued. MarkDropped(txID ids.ID, reason string) GetDropReason(txID ids.ID) (string, bool) + + // TODO: following Banff, these methods can be removed + + // Pre Banff activation, decision transactions are included into + // standard blocks. + HasApricotDecisionTxs() bool + // PeekApricotDecisionTxs returns the next decisionTxs, up to maxTxsBytes, + // without removing them from the mempool. + // It returns nil if !HasApricotDecisionTxs() + PeekApricotDecisionTxs(maxTxsBytes int) []*txs.Tx } // Transactions from clients that have not yet been put into blocks and added to @@ -86,8 +97,7 @@ type mempool struct { bytesAvailable int unissuedDecisionTxs txheap.Heap - unissuedProposalTxs txheap.Heap - unknownTxs prometheus.Counter + unissuedStakerTxs txheap.Heap // Key: Tx ID // Value: String repr. of the verification error @@ -121,31 +131,21 @@ func NewMempool( return nil, err } - unissuedProposalTxs, err := txheap.NewWithMetrics( + unissuedStakerTxs, err := txheap.NewWithMetrics( txheap.NewByStartTime(), - fmt.Sprintf("%s_proposal_txs", namespace), + fmt.Sprintf("%s_staker_txs", namespace), registerer, ) if err != nil { return nil, err } - unknownTxs := prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "unknown_txs_count", - Help: "Number of unknown tx types seen by the mempool", - }) - if err := registerer.Register(unknownTxs); err != nil { - return nil, err - } - bytesAvailableMetric.Set(maxMempoolSize) return &mempool{ bytesAvailableMetric: bytesAvailableMetric, bytesAvailable: maxMempoolSize, unissuedDecisionTxs: unissuedDecisionTxs, - unissuedProposalTxs: unissuedProposalTxs, - unknownTxs: unknownTxs, + unissuedStakerTxs: unissuedStakerTxs, droppedTxIDs: &cache.LRU{Size: droppedTxIDsCacheSize}, consumedUTXOs: ids.NewSet(initialConsumedUTXOsSize), dropIncoming: false, // enable tx adding by default @@ -185,7 +185,7 @@ func (m *mempool) Add(tx *txs.Tx) error { return fmt.Errorf("tx %s conflicts with a transaction in the mempool", txID) } - if err := tx.Unsigned.Visit(&mempoolIssuer{ + if err := tx.Unsigned.Visit(&issuer{ m: m, tx: tx, }); err != nil { @@ -210,24 +210,53 @@ func (m *mempool) Get(txID ids.ID) *txs.Tx { if tx := m.unissuedDecisionTxs.Get(txID); tx != nil { return tx } - return m.unissuedProposalTxs.Get(txID) + return m.unissuedStakerTxs.Get(txID) +} + +func (m *mempool) Remove(txsToRemove []*txs.Tx) { + remover := &remover{ + m: m, + } + + for _, tx := range txsToRemove { + remover.tx = tx + _ = tx.Unsigned.Visit(remover) + } } -func (m *mempool) AddDecisionTx(tx *txs.Tx) { +func (m *mempool) HasTxs() bool { + return m.unissuedDecisionTxs.Len() > 0 || m.unissuedStakerTxs.Len() > 0 +} + +func (m *mempool) PeekTxs(maxTxsBytes int) []*txs.Tx { + txs, size := m.peekApricotDecisionTxs(maxTxsBytes) + + for _, tx := range m.unissuedStakerTxs.List() { + size += len(tx.Bytes()) + if size > maxTxsBytes { + break + } + txs = append(txs, tx) + } + + return txs +} + +func (m *mempool) addDecisionTx(tx *txs.Tx) { m.unissuedDecisionTxs.Add(tx) m.register(tx) } -func (m *mempool) AddProposalTx(tx *txs.Tx) { - m.unissuedProposalTxs.Add(tx) +func (m *mempool) addStakerTx(tx *txs.Tx) { + m.unissuedStakerTxs.Add(tx) m.register(tx) } -func (m *mempool) HasDecisionTxs() bool { return m.unissuedDecisionTxs.Len() > 0 } +func (m *mempool) HasApricotDecisionTxs() bool { return m.unissuedDecisionTxs.Len() > 0 } -func (m *mempool) HasProposalTx() bool { return m.unissuedProposalTxs.Len() > 0 } +func (m *mempool) HasStakerTx() bool { return m.unissuedStakerTxs.Len() > 0 } -func (m *mempool) RemoveDecisionTxs(txs []*txs.Tx) { +func (m *mempool) removeDecisionTxs(txs []*txs.Tx) { for _, tx := range txs { txID := tx.ID() if m.unissuedDecisionTxs.Remove(txID) != nil { @@ -236,34 +265,40 @@ func (m *mempool) RemoveDecisionTxs(txs []*txs.Tx) { } } -func (m *mempool) RemoveProposalTx(tx *txs.Tx) { +func (m *mempool) removeStakerTx(tx *txs.Tx) { txID := tx.ID() - if m.unissuedProposalTxs.Remove(txID) != nil { + if m.unissuedStakerTxs.Remove(txID) != nil { m.deregister(tx) } } -func (m *mempool) PopDecisionTxs(maxTxsBytes int) []*txs.Tx { - var txs []*txs.Tx - for m.unissuedDecisionTxs.Len() > 0 { - tx := m.unissuedDecisionTxs.Peek() - txBytes := tx.Bytes() - if len(txBytes) > maxTxsBytes { - return txs - } - maxTxsBytes -= len(txBytes) +func (m *mempool) PeekApricotDecisionTxs(maxTxsBytes int) []*txs.Tx { + txs, _ := m.peekApricotDecisionTxs(maxTxsBytes) + return txs +} - m.unissuedDecisionTxs.RemoveTop() - m.deregister(tx) - txs = append(txs, tx) +func (m *mempool) peekApricotDecisionTxs(maxTxsBytes int) ([]*txs.Tx, int) { + list := m.unissuedDecisionTxs.List() + + totalBytes, txsToKeep := 0, 0 + for _, tx := range list { + totalBytes += len(tx.Bytes()) + if totalBytes > maxTxsBytes { + break + } + txsToKeep++ } - return txs + + list = list[:txsToKeep] + return list, totalBytes } -func (m *mempool) PopProposalTx() *txs.Tx { - tx := m.unissuedProposalTxs.RemoveTop() - m.deregister(tx) - return tx +func (m *mempool) PeekStakerTx() *txs.Tx { + if m.unissuedStakerTxs.Len() == 0 { + return nil + } + + return m.unissuedStakerTxs.Peek() } func (m *mempool) MarkDropped(txID ids.ID, reason string) { @@ -292,51 +327,3 @@ func (m *mempool) deregister(tx *txs.Tx) { inputs := tx.Unsigned.InputIDs() m.consumedUTXOs.Difference(inputs) } - -type mempoolIssuer struct { - m *mempool - tx *txs.Tx -} - -func (i *mempoolIssuer) AdvanceTimeTx(tx *txs.AdvanceTimeTx) error { - return errCantIssueAdvanceTimeTx -} - -func (i *mempoolIssuer) RewardValidatorTx(tx *txs.RewardValidatorTx) error { - return errCantIssueRewardValidatorTx -} - -func (i *mempoolIssuer) AddValidatorTx(*txs.AddValidatorTx) error { - i.m.AddProposalTx(i.tx) - return nil -} - -func (i *mempoolIssuer) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error { - i.m.AddProposalTx(i.tx) - return nil -} - -func (i *mempoolIssuer) AddDelegatorTx(tx *txs.AddDelegatorTx) error { - i.m.AddProposalTx(i.tx) - return nil -} - -func (i *mempoolIssuer) CreateChainTx(tx *txs.CreateChainTx) error { - i.m.AddDecisionTx(i.tx) - return nil -} - -func (i *mempoolIssuer) CreateSubnetTx(tx *txs.CreateSubnetTx) error { - i.m.AddDecisionTx(i.tx) - return nil -} - -func (i *mempoolIssuer) ImportTx(tx *txs.ImportTx) error { - i.m.AddDecisionTx(i.tx) - return nil -} - -func (i *mempoolIssuer) ExportTx(tx *txs.ExportTx) error { - i.m.AddDecisionTx(i.tx) - return nil -} diff --git a/avalanchego/vms/platformvm/txs/mempool/mempool_test.go b/avalanchego/vms/platformvm/txs/mempool/mempool_test.go index 95b3c861..931f6ece 100644 --- a/avalanchego/vms/platformvm/txs/mempool/mempool_test.go +++ b/avalanchego/vms/platformvm/txs/mempool/mempool_test.go @@ -1,11 +1,13 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package mempool import ( "errors" + "math" "testing" + "time" "github.com/prometheus/client_golang/prometheus" @@ -13,16 +15,18 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) -var _ BlockTimer = &dummyBlkTimer{} +var _ BlockTimer = &noopBlkTimer{} -type dummyBlkTimer struct{} +type noopBlkTimer struct{} -func (bt *dummyBlkTimer) ResetBlockTimer() {} +func (bt *noopBlkTimer) ResetBlockTimer() {} var preFundedKeys = crypto.BuildTestKeys() @@ -32,45 +36,12 @@ func TestBlockBuilderMaxMempoolSizeHandling(t *testing.T) { require := require.New(t) registerer := prometheus.NewRegistry() - mpool, err := NewMempool("mempool", registerer, &dummyBlkTimer{}) + mpool, err := NewMempool("mempool", registerer, &noopBlkTimer{}) require.NoError(err) - // create candidate tx - utx := &txs.CreateChainTx{ - BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ - NetworkID: 10, - BlockchainID: ids.Empty.Prefix(1), - Ins: []*avax.TransferableInput{{ - UTXOID: avax.UTXOID{ - TxID: ids.ID{'t', 'x', 'I', 'D'}, - OutputIndex: 2, - }, - Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, - In: &secp256k1fx.TransferInput{ - Amt: uint64(5678), - Input: secp256k1fx.Input{SigIndices: []uint32{0}}, - }, - }}, - Outs: []*avax.TransferableOutput{{ - Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, - Out: &secp256k1fx.TransferOutput{ - Amt: uint64(1234), - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, - }, - }, - }}, - }}, - SubnetID: ids.ID{'s', 'u', 'b', 'n', 'e', 't'}, - ChainName: "chainName", - VMID: ids.ID{'V', 'M', 'I', 'D'}, - FxIDs: []ids.ID{{'f', 'x', 'I', 'D', 's'}}, - GenesisData: []byte{'g', 'e', 'n', 'D', 'a', 't', 'a'}, - SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{1}}, - } - tx, err := txs.NewSigned(utx, txs.Codec, nil) + decisionTxs, err := createTestDecisionTxs(1) require.NoError(err) + tx := decisionTxs[0] // shortcut to simulated almost filled mempool mpool.(*mempool).bytesAvailable = len(tx.Bytes()) - 1 @@ -84,3 +55,190 @@ func TestBlockBuilderMaxMempoolSizeHandling(t *testing.T) { err = mpool.Add(tx) require.NoError(err, "should have added tx to mempool") } + +func TestDecisionTxsInMempool(t *testing.T) { + require := require.New(t) + + registerer := prometheus.NewRegistry() + mpool, err := NewMempool("mempool", registerer, &noopBlkTimer{}) + require.NoError(err) + + decisionTxs, err := createTestDecisionTxs(2) + require.NoError(err) + + // txs must not already there before we start + require.False(mpool.HasApricotDecisionTxs()) + + for _, tx := range decisionTxs { + // tx not already there + require.False(mpool.Has(tx.ID())) + + // we can insert + require.NoError(mpool.Add(tx)) + + // we can get it + require.True(mpool.Has(tx.ID())) + + retrieved := mpool.Get(tx.ID()) + require.True(retrieved != nil) + require.Equal(tx, retrieved) + + // we can peek it + peeked := mpool.PeekApricotDecisionTxs(math.MaxInt) + + // tx will be among those peeked, + // in NO PARTICULAR ORDER + found := false + for _, pk := range peeked { + if pk.ID() == tx.ID() { + found = true + break + } + } + require.True(found) + + // once removed it cannot be there + mpool.Remove([]*txs.Tx{tx}) + + require.False(mpool.Has(tx.ID())) + require.Equal((*txs.Tx)(nil), mpool.Get(tx.ID())) + + // we can reinsert it again to grow the mempool + require.NoError(mpool.Add(tx)) + } +} + +func TestProposalTxsInMempool(t *testing.T) { + require := require.New(t) + + registerer := prometheus.NewRegistry() + mpool, err := NewMempool("mempool", registerer, &noopBlkTimer{}) + require.NoError(err) + + // The proposal txs are ordered by decreasing start time. This means after + // each insertion, the last inserted transaction should be on the top of the + // heap. + proposalTxs, err := createTestProposalTxs(2) + require.NoError(err) + + // txs should not be already there + require.False(mpool.HasStakerTx()) + + for i, tx := range proposalTxs { + require.False(mpool.Has(tx.ID())) + + // we can insert + require.NoError(mpool.Add(tx)) + + // we can get it + require.True(mpool.HasStakerTx()) + require.True(mpool.Has(tx.ID())) + + retrieved := mpool.Get(tx.ID()) + require.True(retrieved != nil) + require.Equal(tx, retrieved) + + { + // we can peek it + peeked := mpool.PeekStakerTx() + require.True(peeked != nil) + require.Equal(tx, peeked) + } + + { + // we can peek it + peeked := mpool.PeekTxs(math.MaxInt) + require.Len(peeked, i+1) + + // tx will be among those peeked, + // in NO PARTICULAR ORDER + found := false + for _, pk := range peeked { + if pk.ID() == tx.ID() { + found = true + break + } + } + require.True(found) + } + + // once removed it cannot be there + mpool.Remove([]*txs.Tx{tx}) + + require.False(mpool.Has(tx.ID())) + require.Equal((*txs.Tx)(nil), mpool.Get(tx.ID())) + + // we can reinsert it again to grow the mempool + require.NoError(mpool.Add(tx)) + } +} + +func createTestDecisionTxs(count int) ([]*txs.Tx, error) { + decisionTxs := make([]*txs.Tx, 0, count) + for i := uint32(0); i < uint32(count); i++ { + utx := &txs.CreateChainTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: 10, + BlockchainID: ids.Empty.Prefix(uint64(i)), + Ins: []*avax.TransferableInput{{ + UTXOID: avax.UTXOID{ + TxID: ids.ID{'t', 'x', 'I', 'D'}, + OutputIndex: i, + }, + Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, + In: &secp256k1fx.TransferInput{ + Amt: uint64(5678), + Input: secp256k1fx.Input{SigIndices: []uint32{i}}, + }, + }}, + Outs: []*avax.TransferableOutput{{ + Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, + Out: &secp256k1fx.TransferOutput{ + Amt: uint64(1234), + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, + }, + }, + }}, + }}, + SubnetID: ids.GenerateTestID(), + ChainName: "chainName", + VMID: ids.GenerateTestID(), + FxIDs: []ids.ID{ids.GenerateTestID()}, + GenesisData: []byte{'g', 'e', 'n', 'D', 'a', 't', 'a'}, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{1}}, + } + + tx, err := txs.NewSigned(utx, txs.Codec, nil) + if err != nil { + return nil, err + } + decisionTxs = append(decisionTxs, tx) + } + return decisionTxs, nil +} + +// Proposal txs are sorted by decreasing start time +func createTestProposalTxs(count int) ([]*txs.Tx, error) { + var clk mockable.Clock + proposalTxs := make([]*txs.Tx, 0, count) + for i := 0; i < count; i++ { + utx := &txs.AddValidatorTx{ + BaseTx: txs.BaseTx{}, + Validator: validator.Validator{ + Start: uint64(clk.Time().Add(time.Duration(count-i) * time.Second).Unix()), + }, + StakeOuts: nil, + RewardsOwner: &secp256k1fx.OutputOwners{}, + DelegationShares: 100, + } + + tx, err := txs.NewSigned(utx, txs.Codec, nil) + if err != nil { + return nil, err + } + proposalTxs = append(proposalTxs, tx) + } + return proposalTxs, nil +} diff --git a/avalanchego/vms/platformvm/txs/mempool/mock_mempool.go b/avalanchego/vms/platformvm/txs/mempool/mock_mempool.go index 112e70e0..6ed0eefd 100644 --- a/avalanchego/vms/platformvm/txs/mempool/mock_mempool.go +++ b/avalanchego/vms/platformvm/txs/mempool/mock_mempool.go @@ -5,36 +5,37 @@ package mempool import ( + reflect "reflect" + ids "github.com/ava-labs/avalanchego/ids" txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" gomock "github.com/golang/mock/gomock" - reflect "reflect" ) -// MockMempool is a mock of Mempool interface +// MockMempool is a mock of Mempool interface. type MockMempool struct { ctrl *gomock.Controller recorder *MockMempoolMockRecorder } -// MockMempoolMockRecorder is the mock recorder for MockMempool +// MockMempoolMockRecorder is the mock recorder for MockMempool. type MockMempoolMockRecorder struct { mock *MockMempool } -// NewMockMempool creates a new mock instance +// NewMockMempool creates a new mock instance. func NewMockMempool(ctrl *gomock.Controller) *MockMempool { mock := &MockMempool{ctrl: ctrl} mock.recorder = &MockMempoolMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMempool) EXPECT() *MockMempoolMockRecorder { return m.recorder } -// Add mocks base method +// Add mocks base method. func (m *MockMempool) Add(arg0 *txs.Tx) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Add", arg0) @@ -42,61 +43,37 @@ func (m *MockMempool) Add(arg0 *txs.Tx) error { return ret0 } -// Add indicates an expected call of Add +// Add indicates an expected call of Add. func (mr *MockMempoolMockRecorder) Add(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockMempool)(nil).Add), arg0) } -// AddDecisionTx mocks base method -func (m *MockMempool) AddDecisionTx(arg0 *txs.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddDecisionTx", arg0) -} - -// AddDecisionTx indicates an expected call of AddDecisionTx -func (mr *MockMempoolMockRecorder) AddDecisionTx(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTx", reflect.TypeOf((*MockMempool)(nil).AddDecisionTx), arg0) -} - -// AddProposalTx mocks base method -func (m *MockMempool) AddProposalTx(arg0 *txs.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddProposalTx", arg0) -} - -// AddProposalTx indicates an expected call of AddProposalTx -func (mr *MockMempoolMockRecorder) AddProposalTx(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddProposalTx", reflect.TypeOf((*MockMempool)(nil).AddProposalTx), arg0) -} - -// DisableAdding mocks base method +// DisableAdding mocks base method. func (m *MockMempool) DisableAdding() { m.ctrl.T.Helper() m.ctrl.Call(m, "DisableAdding") } -// DisableAdding indicates an expected call of DisableAdding +// DisableAdding indicates an expected call of DisableAdding. func (mr *MockMempoolMockRecorder) DisableAdding() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableAdding", reflect.TypeOf((*MockMempool)(nil).DisableAdding)) } -// EnableAdding mocks base method +// EnableAdding mocks base method. func (m *MockMempool) EnableAdding() { m.ctrl.T.Helper() m.ctrl.Call(m, "EnableAdding") } -// EnableAdding indicates an expected call of EnableAdding +// EnableAdding indicates an expected call of EnableAdding. func (mr *MockMempoolMockRecorder) EnableAdding() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableAdding", reflect.TypeOf((*MockMempool)(nil).EnableAdding)) } -// Get mocks base method +// Get mocks base method. func (m *MockMempool) Get(arg0 ids.ID) *txs.Tx { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", arg0) @@ -104,13 +81,13 @@ func (m *MockMempool) Get(arg0 ids.ID) *txs.Tx { return ret0 } -// Get indicates an expected call of Get +// Get indicates an expected call of Get. func (mr *MockMempoolMockRecorder) Get(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockMempool)(nil).Get), arg0) } -// GetDropReason mocks base method +// GetDropReason mocks base method. func (m *MockMempool) GetDropReason(arg0 ids.ID) (string, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDropReason", arg0) @@ -119,13 +96,13 @@ func (m *MockMempool) GetDropReason(arg0 ids.ID) (string, bool) { return ret0, ret1 } -// GetDropReason indicates an expected call of GetDropReason +// GetDropReason indicates an expected call of GetDropReason. func (mr *MockMempoolMockRecorder) GetDropReason(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDropReason", reflect.TypeOf((*MockMempool)(nil).GetDropReason), arg0) } -// Has mocks base method +// Has mocks base method. func (m *MockMempool) Has(arg0 ids.ID) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Has", arg0) @@ -133,100 +110,116 @@ func (m *MockMempool) Has(arg0 ids.ID) bool { return ret0 } -// Has indicates an expected call of Has +// Has indicates an expected call of Has. func (mr *MockMempoolMockRecorder) Has(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Has", reflect.TypeOf((*MockMempool)(nil).Has), arg0) } -// HasDecisionTxs mocks base method -func (m *MockMempool) HasDecisionTxs() bool { +// HasApricotDecisionTxs mocks base method. +func (m *MockMempool) HasApricotDecisionTxs() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasApricotDecisionTxs") + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasApricotDecisionTxs indicates an expected call of HasApricotDecisionTxs. +func (mr *MockMempoolMockRecorder) HasApricotDecisionTxs() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasApricotDecisionTxs", reflect.TypeOf((*MockMempool)(nil).HasApricotDecisionTxs)) +} + +// HasStakerTx mocks base method. +func (m *MockMempool) HasStakerTx() bool { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HasDecisionTxs") + ret := m.ctrl.Call(m, "HasStakerTx") ret0, _ := ret[0].(bool) return ret0 } -// HasDecisionTxs indicates an expected call of HasDecisionTxs -func (mr *MockMempoolMockRecorder) HasDecisionTxs() *gomock.Call { +// HasStakerTx indicates an expected call of HasStakerTx. +func (mr *MockMempoolMockRecorder) HasStakerTx() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasDecisionTxs", reflect.TypeOf((*MockMempool)(nil).HasDecisionTxs)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasStakerTx", reflect.TypeOf((*MockMempool)(nil).HasStakerTx)) } -// HasProposalTx mocks base method -func (m *MockMempool) HasProposalTx() bool { +// HasTxs mocks base method. +func (m *MockMempool) HasTxs() bool { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HasProposalTx") + ret := m.ctrl.Call(m, "HasTxs") ret0, _ := ret[0].(bool) return ret0 } -// HasProposalTx indicates an expected call of HasProposalTx -func (mr *MockMempoolMockRecorder) HasProposalTx() *gomock.Call { +// HasTxs indicates an expected call of HasTxs. +func (mr *MockMempoolMockRecorder) HasTxs() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasProposalTx", reflect.TypeOf((*MockMempool)(nil).HasProposalTx)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasTxs", reflect.TypeOf((*MockMempool)(nil).HasTxs)) } -// MarkDropped mocks base method +// MarkDropped mocks base method. func (m *MockMempool) MarkDropped(arg0 ids.ID, arg1 string) { m.ctrl.T.Helper() m.ctrl.Call(m, "MarkDropped", arg0, arg1) } -// MarkDropped indicates an expected call of MarkDropped +// MarkDropped indicates an expected call of MarkDropped. func (mr *MockMempoolMockRecorder) MarkDropped(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkDropped", reflect.TypeOf((*MockMempool)(nil).MarkDropped), arg0, arg1) } -// PopDecisionTxs mocks base method -func (m *MockMempool) PopDecisionTxs(arg0 int) []*txs.Tx { +// PeekApricotDecisionTxs mocks base method. +func (m *MockMempool) PeekApricotDecisionTxs(arg0 int) []*txs.Tx { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PopDecisionTxs", arg0) + ret := m.ctrl.Call(m, "PeekApricotDecisionTxs", arg0) ret0, _ := ret[0].([]*txs.Tx) return ret0 } -// PopDecisionTxs indicates an expected call of PopDecisionTxs -func (mr *MockMempoolMockRecorder) PopDecisionTxs(arg0 interface{}) *gomock.Call { +// PeekApricotDecisionTxs indicates an expected call of PeekApricotDecisionTxs. +func (mr *MockMempoolMockRecorder) PeekApricotDecisionTxs(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PopDecisionTxs", reflect.TypeOf((*MockMempool)(nil).PopDecisionTxs), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeekApricotDecisionTxs", reflect.TypeOf((*MockMempool)(nil).PeekApricotDecisionTxs), arg0) } -// PopProposalTx mocks base method -func (m *MockMempool) PopProposalTx() *txs.Tx { +// PeekStakerTx mocks base method. +func (m *MockMempool) PeekStakerTx() *txs.Tx { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PopProposalTx") + ret := m.ctrl.Call(m, "PeekStakerTx") ret0, _ := ret[0].(*txs.Tx) return ret0 } -// PopProposalTx indicates an expected call of PopProposalTx -func (mr *MockMempoolMockRecorder) PopProposalTx() *gomock.Call { +// PeekStakerTx indicates an expected call of PeekStakerTx. +func (mr *MockMempoolMockRecorder) PeekStakerTx() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PopProposalTx", reflect.TypeOf((*MockMempool)(nil).PopProposalTx)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeekStakerTx", reflect.TypeOf((*MockMempool)(nil).PeekStakerTx)) } -// RemoveDecisionTxs mocks base method -func (m *MockMempool) RemoveDecisionTxs(arg0 []*txs.Tx) { +// PeekTxs mocks base method. +func (m *MockMempool) PeekTxs(arg0 int) []*txs.Tx { m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoveDecisionTxs", arg0) + ret := m.ctrl.Call(m, "PeekTxs", arg0) + ret0, _ := ret[0].([]*txs.Tx) + return ret0 } -// RemoveDecisionTxs indicates an expected call of RemoveDecisionTxs -func (mr *MockMempoolMockRecorder) RemoveDecisionTxs(arg0 interface{}) *gomock.Call { +// PeekTxs indicates an expected call of PeekTxs. +func (mr *MockMempoolMockRecorder) PeekTxs(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveDecisionTxs", reflect.TypeOf((*MockMempool)(nil).RemoveDecisionTxs), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeekTxs", reflect.TypeOf((*MockMempool)(nil).PeekTxs), arg0) } -// RemoveProposalTx mocks base method -func (m *MockMempool) RemoveProposalTx(arg0 *txs.Tx) { +// Remove mocks base method. +func (m *MockMempool) Remove(arg0 []*txs.Tx) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoveProposalTx", arg0) + m.ctrl.Call(m, "Remove", arg0) } -// RemoveProposalTx indicates an expected call of RemoveProposalTx -func (mr *MockMempoolMockRecorder) RemoveProposalTx(arg0 interface{}) *gomock.Call { +// Remove indicates an expected call of Remove. +func (mr *MockMempoolMockRecorder) Remove(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveProposalTx", reflect.TypeOf((*MockMempool)(nil).RemoveProposalTx), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockMempool)(nil).Remove), arg0) } diff --git a/avalanchego/vms/platformvm/txs/mempool/remover.go b/avalanchego/vms/platformvm/txs/mempool/remover.go new file mode 100644 index 00000000..decdd712 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/mempool/remover.go @@ -0,0 +1,78 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package mempool + +import "github.com/ava-labs/avalanchego/vms/platformvm/txs" + +var _ txs.Visitor = &remover{} + +type remover struct { + m *mempool + tx *txs.Tx +} + +func (r *remover) AddValidatorTx(*txs.AddValidatorTx) error { + r.m.removeStakerTx(r.tx) + return nil +} + +func (r *remover) AddSubnetValidatorTx(*txs.AddSubnetValidatorTx) error { + r.m.removeStakerTx(r.tx) + return nil +} + +func (r *remover) AddDelegatorTx(*txs.AddDelegatorTx) error { + r.m.removeStakerTx(r.tx) + return nil +} + +func (r *remover) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { + r.m.removeDecisionTxs([]*txs.Tx{r.tx}) + return nil +} + +func (r *remover) CreateChainTx(*txs.CreateChainTx) error { + r.m.removeDecisionTxs([]*txs.Tx{r.tx}) + return nil +} + +func (r *remover) CreateSubnetTx(*txs.CreateSubnetTx) error { + r.m.removeDecisionTxs([]*txs.Tx{r.tx}) + return nil +} + +func (r *remover) ImportTx(*txs.ImportTx) error { + r.m.removeDecisionTxs([]*txs.Tx{r.tx}) + return nil +} + +func (r *remover) ExportTx(*txs.ExportTx) error { + r.m.removeDecisionTxs([]*txs.Tx{r.tx}) + return nil +} + +func (r *remover) TransformSubnetTx(tx *txs.TransformSubnetTx) error { + r.m.removeDecisionTxs([]*txs.Tx{r.tx}) + return nil +} + +func (r *remover) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { + r.m.removeStakerTx(r.tx) + return nil +} + +func (r *remover) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { + r.m.removeStakerTx(r.tx) + return nil +} + +func (r *remover) AdvanceTimeTx(*txs.AdvanceTimeTx) error { + // this tx is never in mempool + return nil +} + +func (r *remover) RewardValidatorTx(*txs.RewardValidatorTx) error { + // this tx is never in mempool + return nil +} diff --git a/avalanchego/vms/platformvm/txs/mock_staker.go b/avalanchego/vms/platformvm/txs/mock_staker.go new file mode 100644 index 00000000..4e9f5307 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/mock_staker.go @@ -0,0 +1,134 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/vms/platformvm/txs (interfaces: Staker) + +// Package txs is a generated GoMock package. +package txs + +import ( + reflect "reflect" + time "time" + + ids "github.com/ava-labs/avalanchego/ids" + gomock "github.com/golang/mock/gomock" +) + +// MockStaker is a mock of Staker interface. +type MockStaker struct { + ctrl *gomock.Controller + recorder *MockStakerMockRecorder +} + +// MockStakerMockRecorder is the mock recorder for MockStaker. +type MockStakerMockRecorder struct { + mock *MockStaker +} + +// NewMockStaker creates a new mock instance. +func NewMockStaker(ctrl *gomock.Controller) *MockStaker { + mock := &MockStaker{ctrl: ctrl} + mock.recorder = &MockStakerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStaker) EXPECT() *MockStakerMockRecorder { + return m.recorder +} + +// CurrentPriority mocks base method. +func (m *MockStaker) CurrentPriority() Priority { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CurrentPriority") + ret0, _ := ret[0].(Priority) + return ret0 +} + +// CurrentPriority indicates an expected call of CurrentPriority. +func (mr *MockStakerMockRecorder) CurrentPriority() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CurrentPriority", reflect.TypeOf((*MockStaker)(nil).CurrentPriority)) +} + +// EndTime mocks base method. +func (m *MockStaker) EndTime() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EndTime") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// EndTime indicates an expected call of EndTime. +func (mr *MockStakerMockRecorder) EndTime() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EndTime", reflect.TypeOf((*MockStaker)(nil).EndTime)) +} + +// NodeID mocks base method. +func (m *MockStaker) NodeID() ids.NodeID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NodeID") + ret0, _ := ret[0].(ids.NodeID) + return ret0 +} + +// NodeID indicates an expected call of NodeID. +func (mr *MockStakerMockRecorder) NodeID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeID", reflect.TypeOf((*MockStaker)(nil).NodeID)) +} + +// PendingPriority mocks base method. +func (m *MockStaker) PendingPriority() Priority { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PendingPriority") + ret0, _ := ret[0].(Priority) + return ret0 +} + +// PendingPriority indicates an expected call of PendingPriority. +func (mr *MockStakerMockRecorder) PendingPriority() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PendingPriority", reflect.TypeOf((*MockStaker)(nil).PendingPriority)) +} + +// StartTime mocks base method. +func (m *MockStaker) StartTime() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StartTime") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// StartTime indicates an expected call of StartTime. +func (mr *MockStakerMockRecorder) StartTime() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartTime", reflect.TypeOf((*MockStaker)(nil).StartTime)) +} + +// SubnetID mocks base method. +func (m *MockStaker) SubnetID() ids.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubnetID") + ret0, _ := ret[0].(ids.ID) + return ret0 +} + +// SubnetID indicates an expected call of SubnetID. +func (mr *MockStakerMockRecorder) SubnetID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubnetID", reflect.TypeOf((*MockStaker)(nil).SubnetID)) +} + +// Weight mocks base method. +func (m *MockStaker) Weight() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Weight") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// Weight indicates an expected call of Weight. +func (mr *MockStakerMockRecorder) Weight() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Weight", reflect.TypeOf((*MockStaker)(nil).Weight)) +} diff --git a/avalanchego/vms/platformvm/txs/priorities.go b/avalanchego/vms/platformvm/txs/priorities.go new file mode 100644 index 00000000..2e7e0161 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/priorities.go @@ -0,0 +1,46 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +const ( + // First primary network apricot delegators are moved from the pending to + // the current validator set, + PrimaryNetworkDelegatorApricotPendingPriority Priority = iota + 1 + // then primary network validators, + PrimaryNetworkValidatorPendingPriority + // then primary network banff delegators, + PrimaryNetworkDelegatorBanffPendingPriority + // then permissionless subnet validators, + SubnetPermissionlessValidatorPendingPriority + // then permissionless subnet delegators. + SubnetPermissionlessDelegatorPendingPriority + // then permissioned subnet validators, + SubnetPermissionedValidatorPendingPriority + + // First permissioned subnet validators are removed from the current + // validator set, + // Invariant: All permissioned stakers must be removed first because they + // are removed by the advancement of time. Permissionless stakers + // are removed with a RewardValidatorTx after time has advanced. + SubnetPermissionedValidatorCurrentPriority + // then permissionless subnet delegators, + SubnetPermissionlessDelegatorCurrentPriority + // then permissionless subnet validators, + SubnetPermissionlessValidatorCurrentPriority + // then primary network delegators, + PrimaryNetworkDelegatorCurrentPriority + // then primary network validators. + PrimaryNetworkValidatorCurrentPriority +) + +var PendingToCurrentPriorities = []Priority{ + PrimaryNetworkDelegatorApricotPendingPriority: PrimaryNetworkDelegatorCurrentPriority, + PrimaryNetworkValidatorPendingPriority: PrimaryNetworkValidatorCurrentPriority, + PrimaryNetworkDelegatorBanffPendingPriority: PrimaryNetworkDelegatorCurrentPriority, + SubnetPermissionlessValidatorPendingPriority: SubnetPermissionlessValidatorCurrentPriority, + SubnetPermissionlessDelegatorPendingPriority: SubnetPermissionlessDelegatorCurrentPriority, + SubnetPermissionedValidatorPendingPriority: SubnetPermissionedValidatorCurrentPriority, +} + +type Priority byte diff --git a/avalanchego/vms/platformvm/txs/remove_subnet_validator_tx.go b/avalanchego/vms/platformvm/txs/remove_subnet_validator_tx.go new file mode 100644 index 00000000..d2d829fb --- /dev/null +++ b/avalanchego/vms/platformvm/txs/remove_subnet_validator_tx.go @@ -0,0 +1,56 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "errors" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/components/verify" +) + +var ( + _ UnsignedTx = &RemoveSubnetValidatorTx{} + + errRemovePrimaryNetworkValidator = errors.New("can't remove primary network validator with RemoveSubnetValidatorTx") +) + +// Removes a validator from a subnet. +type RemoveSubnetValidatorTx struct { + BaseTx `serialize:"true"` + // The node to remove from the subnet. + NodeID ids.NodeID `serialize:"true" json:"nodeID"` + // The subnet to remove the node from. + Subnet ids.ID `serialize:"true" json:"subnetID"` + // Proves that the issuer has the right to remove the node from the subnet. + SubnetAuth verify.Verifiable `serialize:"true" json:"subnetAuthorization"` +} + +func (tx *RemoveSubnetValidatorTx) SyntacticVerify(ctx *snow.Context) error { + switch { + case tx == nil: + return ErrNilTx + case tx.SyntacticallyVerified: + // already passed syntactic verification + return nil + case tx.Subnet == constants.PrimaryNetworkID: + return errRemovePrimaryNetworkValidator + } + + if err := tx.BaseTx.SyntacticVerify(ctx); err != nil { + return err + } + if err := tx.SubnetAuth.Verify(); err != nil { + return err + } + + tx.SyntacticallyVerified = true + return nil +} + +func (tx *RemoveSubnetValidatorTx) Visit(visitor Visitor) error { + return visitor.RemoveSubnetValidatorTx(tx) +} diff --git a/avalanchego/vms/platformvm/txs/remove_subnet_validator_tx_test.go b/avalanchego/vms/platformvm/txs/remove_subnet_validator_tx_test.go new file mode 100644 index 00000000..5fdb9f25 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/remove_subnet_validator_tx_test.go @@ -0,0 +1,161 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "errors" + "testing" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" +) + +func TestRemoveSubnetValidatorTxSyntacticVerify(t *testing.T) { + type test struct { + name string + txFunc func(*gomock.Controller) *RemoveSubnetValidatorTx + shouldErr bool + // If [shouldErr] and [requireSpecificErr] != nil, + // require that the error we get is [requireSpecificErr]. + requireSpecificErr error + } + + var ( + networkID = uint32(1337) + chainID = ids.GenerateTestID() + errInvalidSubnetAuth = errors.New("invalid subnet auth") + ) + + ctx := &snow.Context{ + ChainID: chainID, + NetworkID: networkID, + } + + // A BaseTx that already passed syntactic verification. + verifiedBaseTx := BaseTx{ + SyntacticallyVerified: true, + } + // Sanity check. + require.NoError(t, verifiedBaseTx.SyntacticVerify(ctx)) + + // A BaseTx that passes syntactic verification. + validBaseTx := BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: networkID, + BlockchainID: chainID, + }, + } + // Sanity check. + require.NoError(t, validBaseTx.SyntacticVerify(ctx)) + // Make sure we're not caching the verification result. + require.False(t, validBaseTx.SyntacticallyVerified) + + // A BaseTx that fails syntactic verification. + invalidBaseTx := BaseTx{} + // Sanity check. + require.Error(t, invalidBaseTx.SyntacticVerify(ctx)) + + tests := []test{ + { + name: "nil tx", + txFunc: func(*gomock.Controller) *RemoveSubnetValidatorTx { return nil }, + shouldErr: true, + }, + { + name: "already verified", + txFunc: func(*gomock.Controller) *RemoveSubnetValidatorTx { + return &RemoveSubnetValidatorTx{BaseTx: verifiedBaseTx} + }, + shouldErr: false, + }, + { + name: "invalid BaseTx", + txFunc: func(*gomock.Controller) *RemoveSubnetValidatorTx { + return &RemoveSubnetValidatorTx{ + // Set subnetID so we don't error on that check. + Subnet: ids.GenerateTestID(), + // Set NodeID so we don't error on that check. + NodeID: ids.GenerateTestNodeID(), + BaseTx: invalidBaseTx, + } + }, + shouldErr: true, + }, + { + name: "invalid subnetID", + txFunc: func(*gomock.Controller) *RemoveSubnetValidatorTx { + return &RemoveSubnetValidatorTx{ + BaseTx: validBaseTx, + // Set NodeID so we don't error on that check. + NodeID: ids.GenerateTestNodeID(), + Subnet: constants.PrimaryNetworkID, + } + }, + shouldErr: true, + requireSpecificErr: errRemovePrimaryNetworkValidator, + }, + { + name: "invalid subnetAuth", + txFunc: func(ctrl *gomock.Controller) *RemoveSubnetValidatorTx { + // This SubnetAuth fails verification. + invalidSubnetAuth := verify.NewMockVerifiable(ctrl) + invalidSubnetAuth.EXPECT().Verify().Return(errInvalidSubnetAuth) + return &RemoveSubnetValidatorTx{ + // Set subnetID so we don't error on that check. + Subnet: ids.GenerateTestID(), + // Set NodeID so we don't error on that check. + NodeID: ids.GenerateTestNodeID(), + BaseTx: validBaseTx, + SubnetAuth: invalidSubnetAuth, + } + }, + shouldErr: true, + requireSpecificErr: errInvalidSubnetAuth, + }, + { + name: "passes verification", + txFunc: func(ctrl *gomock.Controller) *RemoveSubnetValidatorTx { + // This SubnetAuth passes verification. + validSubnetAuth := verify.NewMockVerifiable(ctrl) + validSubnetAuth.EXPECT().Verify().Return(nil) + return &RemoveSubnetValidatorTx{ + // Set subnetID so we don't error on that check. + Subnet: ids.GenerateTestID(), + // Set NodeID so we don't error on that check. + NodeID: ids.GenerateTestNodeID(), + BaseTx: validBaseTx, + SubnetAuth: validSubnetAuth, + } + }, + shouldErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + tx := tt.txFunc(ctrl) + err := tx.SyntacticVerify(ctx) + if tt.shouldErr { + require.Error(err) + if tt.requireSpecificErr != nil { + require.ErrorIs(err, tt.requireSpecificErr) + } + return + } + require.NoError(err) + require.True(tx.SyntacticallyVerified) + }) + } +} diff --git a/avalanchego/vms/platformvm/txs/reward_validator_tx.go b/avalanchego/vms/platformvm/txs/reward_validator_tx.go index f872efed..1c03bcb6 100644 --- a/avalanchego/vms/platformvm/txs/reward_validator_tx.go +++ b/avalanchego/vms/platformvm/txs/reward_validator_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -26,7 +26,7 @@ type RewardValidatorTx struct { TxID ids.ID `serialize:"true" json:"txID"` // Marks if this validator should be rewarded according to this node. - ShouldPreferCommit bool + ShouldPreferCommit bool `json:"-"` unsignedBytes []byte // Unsigned byte representation of this data } diff --git a/avalanchego/vms/platformvm/txs/staker_tx.go b/avalanchego/vms/platformvm/txs/staker_tx.go index 09adad87..7039a42d 100644 --- a/avalanchego/vms/platformvm/txs/staker_tx.go +++ b/avalanchego/vms/platformvm/txs/staker_tx.go @@ -1,16 +1,60 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs import ( "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" ) +// ValidatorTx defines the interface for a validator transaction that supports +// delegation. +type ValidatorTx interface { + UnsignedTx + Validator +} + +type DelegatorTx interface { + UnsignedTx + Delegator +} + type StakerTx interface { UnsignedTx + Staker +} + +type Validator interface { + PermissionlessStaker + + ValidationRewardsOwner() fx.Owner + DelegationRewardsOwner() fx.Owner + Shares() uint32 +} + +type Delegator interface { + PermissionlessStaker + + RewardsOwner() fx.Owner +} + +type PermissionlessStaker interface { + Staker + + Outputs() []*avax.TransferableOutput + Stake() []*avax.TransferableOutput +} +type Staker interface { + SubnetID() ids.ID + NodeID() ids.NodeID StartTime() time.Time EndTime() time.Time Weight() uint64 + PendingPriority() Priority + CurrentPriority() Priority } diff --git a/avalanchego/vms/platformvm/txs/transform_subnet_tx.go b/avalanchego/vms/platformvm/txs/transform_subnet_tx.go new file mode 100644 index 00000000..40e560b8 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/transform_subnet_tx.go @@ -0,0 +1,170 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "errors" + "fmt" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" +) + +var ( + _ UnsignedTx = &TransformSubnetTx{} + + errCantTransformPrimaryNetwork = errors.New("cannot transform primary network") + errEmptyAssetID = errors.New("empty asset ID is not valid") + errAssetIDCantBeAVAX = errors.New("asset ID can't be AVAX") + errInitialSupplyZero = errors.New("initial supply must be non-0") + errInitialSupplyGreaterThanMaxSupply = errors.New("initial supply can't be greater than maximum supply") + errMinConsumptionRateTooLarge = errors.New("min consumption rate must be less than or equal to max consumption rate") + errMaxConsumptionRateTooLarge = fmt.Errorf("max consumption rate must be less than or equal to %d", reward.PercentDenominator) + errMinValidatorStakeZero = errors.New("min validator stake must be non-0") + errMinValidatorStakeAboveSupply = errors.New("min validator stake must be less than or equal to initial supply") + errMinValidatorStakeAboveMax = errors.New("min validator stake must be less than or equal to max validator stake") + errMaxValidatorStakeTooLarge = errors.New("max validator stake must be less than or equal to max supply") + errMinStakeDurationZero = errors.New("min stake duration must be non-0") + errMinStakeDurationTooLarge = errors.New("min stake duration must be less than or equal to max stake duration") + errMinDelegationFeeTooLarge = fmt.Errorf("min delegation fee must be less than or equal to %d", reward.PercentDenominator) + errMinDelegatorStakeZero = errors.New("min delegator stake must be non-0") + errMaxValidatorWeightFactorZero = errors.New("max validator weight factor must be non-0") + errUptimeRequirementTooLarge = fmt.Errorf("uptime requirement must be less than or equal to %d", reward.PercentDenominator) +) + +// TransformSubnetTx is an unsigned transformSubnetTx +type TransformSubnetTx struct { + // Metadata, inputs and outputs + BaseTx `serialize:"true"` + // ID of the Subnet to transform + // Restrictions: + // - Must not be the Primary Network ID + Subnet ids.ID `serialize:"true" json:"subnetID"` + // Asset to use when staking on the Subnet + // Restrictions: + // - Must not be the Empty ID + // - Must not be the AVAX ID + AssetID ids.ID `serialize:"true" json:"assetID"` + // Amount to initially specify as the current supply + // Restrictions: + // - Must be > 0 + InitialSupply uint64 `serialize:"true" json:"initialSupply"` + // Amount to specify as the maximum token supply + // Restrictions: + // - Must be >= [InitialSupply] + MaximumSupply uint64 `serialize:"true" json:"maximumSupply"` + // MinConsumptionRate is the rate to allocate funds if the validator's stake + // duration is 0 + MinConsumptionRate uint64 `serialize:"true" json:"minConsumptionRate"` + // MaxConsumptionRate is the rate to allocate funds if the validator's stake + // duration is equal to the minting period + // Restrictions: + // - Must be >= [MinConsumptionRate] + // - Must be <= [reward.PercentDenominator] + MaxConsumptionRate uint64 `serialize:"true" json:"maxConsumptionRate"` + // MinValidatorStake is the minimum amount of funds required to become a + // validator. + // Restrictions: + // - Must be > 0 + // - Must be <= [InitialSupply] + MinValidatorStake uint64 `serialize:"true" json:"minValidatorStake"` + // MaxValidatorStake is the maximum amount of funds a single validator can + // be allocated, including delegated funds. + // Restrictions: + // - Must be >= [MinValidatorStake] + // - Must be <= [MaximumSupply] + MaxValidatorStake uint64 `serialize:"true" json:"maxValidatorStake"` + // MinStakeDuration is the minimum number of seconds a staker can stake for. + // Restrictions: + // - Must be > 0 + MinStakeDuration uint32 `serialize:"true" json:"minStakeDuration"` + // MaxStakeDuration is the maximum number of seconds a staker can stake for. + // Restrictions: + // - Must be >= [MinStakeDuration] + // - Must be <= [GlobalMaxStakeDuration] + MaxStakeDuration uint32 `serialize:"true" json:"maxStakeDuration"` + // MinDelegationFee is the minimum percentage a validator must charge a + // delegator for delegating. + // Restrictions: + // - Must be <= [reward.PercentDenominator] + MinDelegationFee uint32 `serialize:"true" json:"minDelegationFee"` + // MinDelegatorStake is the minimum amount of funds required to become a + // delegator. + // Restrictions: + // - Must be > 0 + MinDelegatorStake uint64 `serialize:"true" json:"minDelegatorStake"` + // MaxValidatorWeightFactor is the factor which calculates the maximum + // amount of delegation a validator can receive. + // Note: a value of 1 effectively disables delegation. + // Restrictions: + // - Must be > 0 + MaxValidatorWeightFactor byte `serialize:"true" json:"maxValidatorWeightFactor"` + // UptimeRequirement is the minimum percentage a validator must be online + // and responsive to receive a reward. + // Restrictions: + // - Must be <= [reward.PercentDenominator] + UptimeRequirement uint32 `serialize:"true" json:"uptimeRequirement"` + // Authorizes this transformation + SubnetAuth verify.Verifiable `serialize:"true" json:"subnetAuthorization"` +} + +func (tx *TransformSubnetTx) SyntacticVerify(ctx *snow.Context) error { + switch { + case tx == nil: + return ErrNilTx + case tx.SyntacticallyVerified: // already passed syntactic verification + return nil + case tx.Subnet == constants.PrimaryNetworkID: + return errCantTransformPrimaryNetwork + case tx.AssetID == ids.Empty: + return errEmptyAssetID + case tx.AssetID == ctx.AVAXAssetID: + return errAssetIDCantBeAVAX + case tx.InitialSupply == 0: + return errInitialSupplyZero + case tx.InitialSupply > tx.MaximumSupply: + return errInitialSupplyGreaterThanMaxSupply + case tx.MinConsumptionRate > tx.MaxConsumptionRate: + return errMinConsumptionRateTooLarge + case tx.MaxConsumptionRate > reward.PercentDenominator: + return errMaxConsumptionRateTooLarge + case tx.MinValidatorStake == 0: + return errMinValidatorStakeZero + case tx.MinValidatorStake > tx.InitialSupply: + return errMinValidatorStakeAboveSupply + case tx.MinValidatorStake > tx.MaxValidatorStake: + return errMinValidatorStakeAboveMax + case tx.MaxValidatorStake > tx.MaximumSupply: + return errMaxValidatorStakeTooLarge + case tx.MinStakeDuration == 0: + return errMinStakeDurationZero + case tx.MinStakeDuration > tx.MaxStakeDuration: + return errMinStakeDurationTooLarge + case tx.MinDelegationFee > reward.PercentDenominator: + return errMinDelegationFeeTooLarge + case tx.MinDelegatorStake == 0: + return errMinDelegatorStakeZero + case tx.MaxValidatorWeightFactor == 0: + return errMaxValidatorWeightFactorZero + case tx.UptimeRequirement > reward.PercentDenominator: + return errUptimeRequirementTooLarge + } + + if err := tx.BaseTx.SyntacticVerify(ctx); err != nil { + return err + } + if err := tx.SubnetAuth.Verify(); err != nil { + return err + } + + tx.SyntacticallyVerified = true + return nil +} + +func (tx *TransformSubnetTx) Visit(visitor Visitor) error { + return visitor.TransformSubnetTx(tx) +} diff --git a/avalanchego/vms/platformvm/txs/transform_subnet_tx_test.go b/avalanchego/vms/platformvm/txs/transform_subnet_tx_test.go new file mode 100644 index 00000000..1a1abd01 --- /dev/null +++ b/avalanchego/vms/platformvm/txs/transform_subnet_tx_test.go @@ -0,0 +1,440 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txs + +import ( + "errors" + "testing" + + "github.com/golang/mock/gomock" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" +) + +func TestTransformSubnetTxSyntacticVerify(t *testing.T) { + type test struct { + name string + txFunc func(*gomock.Controller) *TransformSubnetTx + err error + } + + var ( + networkID = uint32(1337) + chainID = ids.GenerateTestID() + errInvalidSubnetAuth = errors.New("invalid subnet auth") + ) + + ctx := &snow.Context{ + ChainID: chainID, + NetworkID: networkID, + AVAXAssetID: ids.GenerateTestID(), + } + + // A BaseTx that already passed syntactic verification. + verifiedBaseTx := BaseTx{ + SyntacticallyVerified: true, + } + + // A BaseTx that passes syntactic verification. + validBaseTx := BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: networkID, + BlockchainID: chainID, + }, + } + + // A BaseTx that fails syntactic verification. + invalidBaseTx := BaseTx{} + + tests := []test{ + { + name: "nil tx", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return nil + }, + err: ErrNilTx, + }, + { + name: "already verified", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: verifiedBaseTx, + } + }, + err: nil, + }, + { + name: "invalid subnetID", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: constants.PrimaryNetworkID, + } + }, + err: errCantTransformPrimaryNetwork, + }, + { + name: "empty assetID", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.Empty, + } + }, + err: errEmptyAssetID, + }, + { + name: "AVAX assetID", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ctx.AVAXAssetID, + } + }, + err: errAssetIDCantBeAVAX, + }, + { + name: "initialSupply == 0", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 0, + } + }, + err: errInitialSupplyZero, + }, + { + name: "initialSupply > maximumSupply", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 2, + MaximumSupply: 1, + } + }, + err: errInitialSupplyGreaterThanMaxSupply, + }, + { + name: "minConsumptionRate > maxConsumptionRate", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 1, + MaximumSupply: 1, + MinConsumptionRate: 2, + MaxConsumptionRate: 1, + } + }, + err: errMinConsumptionRateTooLarge, + }, + { + name: "maxConsumptionRate > 100%", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 1, + MaximumSupply: 1, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator + 1, + } + }, + err: errMaxConsumptionRateTooLarge, + }, + { + name: "minValidatorStake == 0", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 1, + MaximumSupply: 1, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 0, + } + }, + err: errMinValidatorStakeZero, + }, + { + name: "minValidatorStake > initialSupply", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 1, + MaximumSupply: 1, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + } + }, + err: errMinValidatorStakeAboveSupply, + }, + { + name: "minValidatorStake > maxValidatorStake", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 1, + } + }, + err: errMinValidatorStakeAboveMax, + }, + { + name: "maxValidatorStake > maximumSupply", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 11, + } + }, + err: errMaxValidatorStakeTooLarge, + }, + { + name: "minStakeDuration == 0", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 0, + } + }, + err: errMinStakeDurationZero, + }, + { + name: "minStakeDuration > maxStakeDuration", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 2, + MaxStakeDuration: 1, + } + }, + err: errMinStakeDurationTooLarge, + }, + { + name: "minDelegationFee > 100%", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator + 1, + } + }, + err: errMinDelegationFeeTooLarge, + }, + { + name: "minDelegatorStake == 0", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator, + MinDelegatorStake: 0, + } + }, + err: errMinDelegatorStakeZero, + }, + { + name: "maxValidatorWeightFactor == 0", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator, + MinDelegatorStake: 1, + MaxValidatorWeightFactor: 0, + } + }, + err: errMaxValidatorWeightFactorZero, + }, + { + name: "uptimeRequirement > 100%", + txFunc: func(*gomock.Controller) *TransformSubnetTx { + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator, + MinDelegatorStake: 1, + MaxValidatorWeightFactor: 1, + UptimeRequirement: reward.PercentDenominator + 1, + } + }, + err: errUptimeRequirementTooLarge, + }, + { + name: "invalid subnetAuth", + txFunc: func(ctrl *gomock.Controller) *TransformSubnetTx { + // This SubnetAuth fails verification. + invalidSubnetAuth := verify.NewMockVerifiable(ctrl) + invalidSubnetAuth.EXPECT().Verify().Return(errInvalidSubnetAuth) + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator, + MinDelegatorStake: 1, + MaxValidatorWeightFactor: 1, + UptimeRequirement: reward.PercentDenominator, + SubnetAuth: invalidSubnetAuth, + } + }, + err: errInvalidSubnetAuth, + }, + { + name: "passes verification", + txFunc: func(ctrl *gomock.Controller) *TransformSubnetTx { + // This SubnetAuth passes verification. + validSubnetAuth := verify.NewMockVerifiable(ctrl) + validSubnetAuth.EXPECT().Verify().Return(nil) + return &TransformSubnetTx{ + BaseTx: validBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator, + MinDelegatorStake: 1, + MaxValidatorWeightFactor: 1, + UptimeRequirement: reward.PercentDenominator, + SubnetAuth: validSubnetAuth, + } + }, + err: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + tx := tt.txFunc(ctrl) + err := tx.SyntacticVerify(ctx) + require.ErrorIs(err, tt.err) + }) + } + + t.Run("invalid BaseTx", func(t *testing.T) { + require := require.New(t) + tx := &TransformSubnetTx{ + BaseTx: invalidBaseTx, + Subnet: ids.GenerateTestID(), + AssetID: ids.GenerateTestID(), + InitialSupply: 10, + MaximumSupply: 10, + MinConsumptionRate: 0, + MaxConsumptionRate: reward.PercentDenominator, + MinValidatorStake: 2, + MaxValidatorStake: 10, + MinStakeDuration: 1, + MaxStakeDuration: 2, + MinDelegationFee: reward.PercentDenominator, + MinDelegatorStake: 1, + MaxValidatorWeightFactor: 1, + UptimeRequirement: reward.PercentDenominator, + } + err := tx.SyntacticVerify(ctx) + require.Error(err) + }) +} diff --git a/avalanchego/vms/platformvm/txs/tx.go b/avalanchego/vms/platformvm/txs/tx.go index 6dc00725..fac73c71 100644 --- a/avalanchego/vms/platformvm/txs/tx.go +++ b/avalanchego/vms/platformvm/txs/tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -18,7 +18,8 @@ import ( ) var ( - errNilSignedTx = errors.New("nil signed tx is not valid") + ErrNilSignedTx = errors.New("nil signed tx is not valid") + errSignedTxNotInitialized = errors.New("signed tx was never initialized and is not valid") ) @@ -43,7 +44,9 @@ func NewSigned( return res, res.Sign(c, signers) } -// Parse signed tx starting from its byte representation +// Parse signed tx starting from its byte representation. +// Note: We explicitly pass the codec in Parse since we may need to parse +// P-Chain genesis txs whose length exceed the max length of txs.Codec. func Parse(c codec.Manager, signedBytes []byte) (*Tx, error) { tx := &Tx{} if _, err := c.Unmarshal(signedBytes, tx); err != nil { @@ -87,7 +90,7 @@ func (tx *Tx) UTXOs() []*avax.UTXO { func (tx *Tx) SyntacticVerify(ctx *snow.Context) error { switch { case tx == nil: - return errNilSignedTx + return ErrNilSignedTx case tx.id == ids.Empty: return errSignedTxNotInitialized default: @@ -96,6 +99,8 @@ func (tx *Tx) SyntacticVerify(ctx *snow.Context) error { } // Sign this transaction with the provided signers +// Note: We explicitly pass the codec in Sign since we may need to sign P-Chain +// genesis txs whose length exceed the max length of txs.Codec. func (tx *Tx) Sign(c codec.Manager, signers [][]*crypto.PrivateKeySECP256K1R) error { unsignedBytes, err := c.Marshal(Version, &tx.Unsigned) if err != nil { diff --git a/avalanchego/vms/platformvm/txs/txheap/by_age.go b/avalanchego/vms/platformvm/txs/txheap/by_age.go index 5152e1f0..a4da6778 100644 --- a/avalanchego/vms/platformvm/txs/txheap/by_age.go +++ b/avalanchego/vms/platformvm/txs/txheap/by_age.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txheap diff --git a/avalanchego/vms/platformvm/txs/txheap/by_end_time.go b/avalanchego/vms/platformvm/txs/txheap/by_end_time.go index ed3b00c9..9bc4e3f2 100644 --- a/avalanchego/vms/platformvm/txs/txheap/by_end_time.go +++ b/avalanchego/vms/platformvm/txs/txheap/by_end_time.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txheap @@ -22,11 +22,11 @@ func NewByEndTime() TimedHeap { } func (h *byEndTime) Less(i, j int) bool { - iTime := h.txs[i].tx.Unsigned.(txs.StakerTx).EndTime() - jTime := h.txs[j].tx.Unsigned.(txs.StakerTx).EndTime() + iTime := h.txs[i].tx.Unsigned.(txs.Staker).EndTime() + jTime := h.txs[j].tx.Unsigned.(txs.Staker).EndTime() return iTime.Before(jTime) } func (h *byEndTime) Timestamp() time.Time { - return h.Peek().Unsigned.(txs.StakerTx).EndTime() + return h.Peek().Unsigned.(txs.Staker).EndTime() } diff --git a/avalanchego/vms/platformvm/txs/txheap/by_end_time_test.go b/avalanchego/vms/platformvm/txs/txheap/by_end_time_test.go index c9cf8d8d..1c8d484a 100644 --- a/avalanchego/vms/platformvm/txs/txheap/by_end_time_test.go +++ b/avalanchego/vms/platformvm/txs/txheap/by_end_time_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txheap diff --git a/avalanchego/vms/platformvm/txs/txheap/by_start_time.go b/avalanchego/vms/platformvm/txs/txheap/by_start_time.go index 1de2f59b..a816ecff 100644 --- a/avalanchego/vms/platformvm/txs/txheap/by_start_time.go +++ b/avalanchego/vms/platformvm/txs/txheap/by_start_time.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txheap @@ -28,11 +28,11 @@ func NewByStartTime() TimedHeap { } func (h *byStartTime) Less(i, j int) bool { - iTime := h.txs[i].tx.Unsigned.(txs.StakerTx).StartTime() - jTime := h.txs[j].tx.Unsigned.(txs.StakerTx).StartTime() + iTime := h.txs[i].tx.Unsigned.(txs.Staker).StartTime() + jTime := h.txs[j].tx.Unsigned.(txs.Staker).StartTime() return iTime.Before(jTime) } func (h *byStartTime) Timestamp() time.Time { - return h.Peek().Unsigned.(txs.StakerTx).StartTime() + return h.Peek().Unsigned.(txs.Staker).StartTime() } diff --git a/avalanchego/vms/platformvm/txs/txheap/by_start_time_test.go b/avalanchego/vms/platformvm/txs/txheap/by_start_time_test.go index ba5d2b6e..d8135346 100644 --- a/avalanchego/vms/platformvm/txs/txheap/by_start_time_test.go +++ b/avalanchego/vms/platformvm/txs/txheap/by_start_time_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txheap diff --git a/avalanchego/vms/platformvm/txs/txheap/heap.go b/avalanchego/vms/platformvm/txs/txheap/heap.go index 20e59eed..62d94f42 100644 --- a/avalanchego/vms/platformvm/txs/txheap/heap.go +++ b/avalanchego/vms/platformvm/txs/txheap/heap.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txheap @@ -111,6 +111,7 @@ func (h *txHeap) Push(x interface{}) { func (h *txHeap) Pop() interface{} { newLen := len(h.txs) - 1 htx := h.txs[newLen] + h.txs[newLen] = nil h.txs = h.txs[:newLen] tx := htx.tx diff --git a/avalanchego/vms/platformvm/txs/txheap/with_metrics.go b/avalanchego/vms/platformvm/txs/txheap/with_metrics.go index c8d22f0c..d652c116 100644 --- a/avalanchego/vms/platformvm/txs/txheap/with_metrics.go +++ b/avalanchego/vms/platformvm/txs/txheap/with_metrics.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txheap diff --git a/avalanchego/vms/platformvm/txs/unsigned_tx.go b/avalanchego/vms/platformvm/txs/unsigned_tx.go index 0176b0d0..3a40aa03 100644 --- a/avalanchego/vms/platformvm/txs/unsigned_tx.go +++ b/avalanchego/vms/platformvm/txs/unsigned_tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -7,6 +7,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) // UnsignedTx is an unsigned transaction @@ -14,8 +15,8 @@ type UnsignedTx interface { // TODO: Remove this initialization pattern from both the platformvm and the // avm. snow.ContextInitializable + secp256k1fx.UnsignedTx Initialize(unsignedBytes []byte) - Bytes() []byte // InputIDs returns the set of inputs this transaction consumes InputIDs() ids.Set diff --git a/avalanchego/vms/platformvm/txs/visitor.go b/avalanchego/vms/platformvm/txs/visitor.go index ce1d85ad..b7398c43 100644 --- a/avalanchego/vms/platformvm/txs/visitor.go +++ b/avalanchego/vms/platformvm/txs/visitor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package txs @@ -14,4 +14,8 @@ type Visitor interface { ExportTx(*ExportTx) error AdvanceTimeTx(*AdvanceTimeTx) error RewardValidatorTx(*RewardValidatorTx) error + RemoveSubnetValidatorTx(*RemoveSubnetValidatorTx) error + TransformSubnetTx(*TransformSubnetTx) error + AddPermissionlessValidatorTx(*AddPermissionlessValidatorTx) error + AddPermissionlessDelegatorTx(*AddPermissionlessDelegatorTx) error } diff --git a/avalanchego/vms/platformvm/utxo/handler.go b/avalanchego/vms/platformvm/utxo/handler.go index 391b4be7..bf61386a 100644 --- a/avalanchego/vms/platformvm/utxo/handler.go +++ b/avalanchego/vms/platformvm/utxo/handler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utxo diff --git a/avalanchego/vms/platformvm/utxo/handler_test.go b/avalanchego/vms/platformvm/utxo/handler_test.go index ffa147c5..da538ab3 100644 --- a/avalanchego/vms/platformvm/utxo/handler_test.go +++ b/avalanchego/vms/platformvm/utxo/handler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package utxo diff --git a/avalanchego/vms/platformvm/utxo/mock_verifier.go b/avalanchego/vms/platformvm/utxo/mock_verifier.go new file mode 100644 index 00000000..6dc2b57a --- /dev/null +++ b/avalanchego/vms/platformvm/utxo/mock_verifier.go @@ -0,0 +1,67 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/vms/platformvm/utxo (interfaces: Verifier) + +// Package utxo is a generated GoMock package. +package utxo + +import ( + reflect "reflect" + + ids "github.com/ava-labs/avalanchego/ids" + avax "github.com/ava-labs/avalanchego/vms/components/avax" + verify "github.com/ava-labs/avalanchego/vms/components/verify" + state "github.com/ava-labs/avalanchego/vms/platformvm/state" + txs "github.com/ava-labs/avalanchego/vms/platformvm/txs" + gomock "github.com/golang/mock/gomock" +) + +// MockVerifier is a mock of Verifier interface. +type MockVerifier struct { + ctrl *gomock.Controller + recorder *MockVerifierMockRecorder +} + +// MockVerifierMockRecorder is the mock recorder for MockVerifier. +type MockVerifierMockRecorder struct { + mock *MockVerifier +} + +// NewMockVerifier creates a new mock instance. +func NewMockVerifier(ctrl *gomock.Controller) *MockVerifier { + mock := &MockVerifier{ctrl: ctrl} + mock.recorder = &MockVerifierMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVerifier) EXPECT() *MockVerifierMockRecorder { + return m.recorder +} + +// VerifySpend mocks base method. +func (m *MockVerifier) VerifySpend(arg0 txs.UnsignedTx, arg1 state.UTXOGetter, arg2 []*avax.TransferableInput, arg3 []*avax.TransferableOutput, arg4 []verify.Verifiable, arg5 map[ids.ID]uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifySpend", arg0, arg1, arg2, arg3, arg4, arg5) + ret0, _ := ret[0].(error) + return ret0 +} + +// VerifySpend indicates an expected call of VerifySpend. +func (mr *MockVerifierMockRecorder) VerifySpend(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifySpend", reflect.TypeOf((*MockVerifier)(nil).VerifySpend), arg0, arg1, arg2, arg3, arg4, arg5) +} + +// VerifySpendUTXOs mocks base method. +func (m *MockVerifier) VerifySpendUTXOs(arg0 txs.UnsignedTx, arg1 []*avax.UTXO, arg2 []*avax.TransferableInput, arg3 []*avax.TransferableOutput, arg4 []verify.Verifiable, arg5 map[ids.ID]uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifySpendUTXOs", arg0, arg1, arg2, arg3, arg4, arg5) + ret0, _ := ret[0].(error) + return ret0 +} + +// VerifySpendUTXOs indicates an expected call of VerifySpendUTXOs. +func (mr *MockVerifierMockRecorder) VerifySpendUTXOs(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifySpendUTXOs", reflect.TypeOf((*MockVerifier)(nil).VerifySpendUTXOs), arg0, arg1, arg2, arg3, arg4, arg5) +} diff --git a/avalanchego/vms/platformvm/validator/heap.go b/avalanchego/vms/platformvm/validator/heap.go deleted file mode 100644 index 261464ea..00000000 --- a/avalanchego/vms/platformvm/validator/heap.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package validator - -import "container/heap" - -// EndTimeHeap orders validators by EndTime from earliest to latest. -type EndTimeHeap []*Validator - -func (h *EndTimeHeap) Len() int { return len(*h) } -func (h *EndTimeHeap) Less(i, j int) bool { return (*h)[i].EndTime().Before((*h)[j].EndTime()) } -func (h *EndTimeHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } -func (h *EndTimeHeap) Add(validator *Validator) { heap.Push(h, validator) } -func (h *EndTimeHeap) Peek() *Validator { return (*h)[0] } -func (h *EndTimeHeap) Remove() *Validator { return heap.Pop(h).(*Validator) } -func (h *EndTimeHeap) Push(x interface{}) { *h = append(*h, x.(*Validator)) } -func (h *EndTimeHeap) Pop() interface{} { - newLen := len(*h) - 1 - val := (*h)[newLen] - (*h)[newLen] = nil - *h = (*h)[:newLen] - return val -} diff --git a/avalanchego/vms/platformvm/validator/subnet_validator.go b/avalanchego/vms/platformvm/validator/subnet_validator.go index 03bb2083..88045d1b 100644 --- a/avalanchego/vms/platformvm/validator/subnet_validator.go +++ b/avalanchego/vms/platformvm/validator/subnet_validator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validator @@ -13,7 +13,7 @@ type SubnetValidator struct { Validator `serialize:"true"` // ID of the subnet this validator is validating - Subnet ids.ID `serialize:"true" json:"subnet"` + Subnet ids.ID `serialize:"true" json:"subnetID"` } // SubnetID is the ID of the subnet this validator is validating diff --git a/avalanchego/vms/platformvm/validator/validator.go b/avalanchego/vms/platformvm/validator/validator.go index 2155a19d..9ae95eac 100644 --- a/avalanchego/vms/platformvm/validator/validator.go +++ b/avalanchego/vms/platformvm/validator/validator.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validator diff --git a/avalanchego/vms/platformvm/validator/validator_test.go b/avalanchego/vms/platformvm/validator/validator_test.go index 99a58901..8175c5a7 100644 --- a/avalanchego/vms/platformvm/validator/validator_test.go +++ b/avalanchego/vms/platformvm/validator/validator_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validator diff --git a/avalanchego/vms/platformvm/vm.go b/avalanchego/vms/platformvm/vm.go index 70b23cb9..37f3159c 100644 --- a/avalanchego/vms/platformvm/vm.go +++ b/avalanchego/vms/platformvm/vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm @@ -98,7 +98,7 @@ type VM struct { validatorSetCaches map[ids.ID]cache.Cacher // sliding window of blocks that were recently accepted - recentlyAccepted *window.Window + recentlyAccepted window.Window[ids.ID] txBuilder txbuilder.Builder txExecutorBackend *txexecutor.Backend @@ -119,9 +119,6 @@ func (vm *VM) Initialize( ) error { ctx.Log.Verbo("initializing platform chain") - // SGB-MERGE: This is a new line of code - validators.InitializeDefaultValidators(ctx.NetworkID) - registerer := prometheus.NewRegistry() if err := ctx.Metrics.Register(registerer); err != nil { return err @@ -144,7 +141,7 @@ func (vm *VM) Initialize( } vm.validatorSetCaches = make(map[ids.ID]cache.Cacher) - vm.recentlyAccepted = window.New( + vm.recentlyAccepted = window.New[ids.ID]( window.Config{ Clock: &vm.clock, MaxSize: maxRecentlyAcceptedWindowSize, @@ -173,7 +170,7 @@ func (vm *VM) Initialize( vm.txBuilder = txbuilder.New( vm.ctx, - vm.Config, + &vm.Config, &vm.clock, vm.fx, vm.state, @@ -550,7 +547,7 @@ func (vm *VM) GetMinimumHeight() (uint64, error) { return vm.GetCurrentHeight() } - blk, err := vm.GetBlock(oldest.(ids.ID)) + blk, err := vm.GetBlock(oldest) if err != nil { return 0, err } diff --git a/avalanchego/vms/platformvm/vm_regression_test.go b/avalanchego/vms/platformvm/vm_regression_test.go index ea37142c..0083fcfe 100644 --- a/avalanchego/vms/platformvm/vm_regression_test.go +++ b/avalanchego/vms/platformvm/vm_regression_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm @@ -24,6 +24,7 @@ import ( "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" @@ -69,14 +70,14 @@ func TestAddDelegatorTxOverDelegatedRegression(t *testing.T) { // trigger block creation require.NoError(vm.Builder.AddUnverifiedTx(addValidatorTx)) - addValidatorBlock, err := vm.BuildBlock() + addValidatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addValidatorBlock) vm.clock.Set(validatorStartTime) - firstAdvanceTimeBlock, err := vm.BuildBlock() + firstAdvanceTimeBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, firstAdvanceTimeBlock) @@ -99,14 +100,14 @@ func TestAddDelegatorTxOverDelegatedRegression(t *testing.T) { // trigger block creation require.NoError(vm.Builder.AddUnverifiedTx(addFirstDelegatorTx)) - addFirstDelegatorBlock, err := vm.BuildBlock() + addFirstDelegatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addFirstDelegatorBlock) vm.clock.Set(firstDelegatorStartTime) - secondAdvanceTimeBlock, err := vm.BuildBlock() + secondAdvanceTimeBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, secondAdvanceTimeBlock) @@ -131,7 +132,7 @@ func TestAddDelegatorTxOverDelegatedRegression(t *testing.T) { // trigger block creation require.NoError(vm.Builder.AddUnverifiedTx(addSecondDelegatorTx)) - addSecondDelegatorBlock, err := vm.BuildBlock() + addSecondDelegatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addSecondDelegatorBlock) @@ -233,7 +234,7 @@ func TestAddDelegatorTxHeapCorruption(t *testing.T) { require.NoError(err) // trigger block creation for the validator tx - addValidatorBlock, err := vm.BuildBlock() + addValidatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addValidatorBlock) @@ -255,7 +256,7 @@ func TestAddDelegatorTxHeapCorruption(t *testing.T) { require.NoError(err) // trigger block creation for the first add delegator tx - addFirstDelegatorBlock, err := vm.BuildBlock() + addFirstDelegatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addFirstDelegatorBlock) @@ -277,7 +278,7 @@ func TestAddDelegatorTxHeapCorruption(t *testing.T) { require.NoError(err) // trigger block creation for the second add delegator tx - addSecondDelegatorBlock, err := vm.BuildBlock() + addSecondDelegatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addSecondDelegatorBlock) @@ -299,7 +300,7 @@ func TestAddDelegatorTxHeapCorruption(t *testing.T) { require.NoError(err) // trigger block creation for the third add delegator tx - addThirdDelegatorBlock, err := vm.BuildBlock() + addThirdDelegatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addThirdDelegatorBlock) @@ -321,7 +322,7 @@ func TestAddDelegatorTxHeapCorruption(t *testing.T) { require.NoError(err) // trigger block creation for the fourth add delegator tx - addFourthDelegatorBlock, err := vm.BuildBlock() + addFourthDelegatorBlock, err := vm.Builder.BuildBlock() if test.shouldFail { require.Error(err, "should have failed to allow new delegator") @@ -351,6 +352,7 @@ func TestUnverifiedParentPanicRegression(t *testing.T) { MinStakeDuration: defaultMinStakingDuration, MaxStakeDuration: defaultMaxStakingDuration, RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, // Banff not yet active }, }} @@ -407,14 +409,14 @@ func TestUnverifiedParentPanicRegression(t *testing.T) { t.Fatal(err) } - preferred, err := vm.Preferred() + preferred, err := vm.Builder.Preferred() if err != nil { t.Fatal(err) } preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessStandardBlk, err := blocks.NewStandardBlock( + statelessStandardBlk, err := blocks.NewApricotStandardBlock( preferredID, preferredHeight+1, []*txs.Tx{addSubnetTx0}, @@ -424,7 +426,7 @@ func TestUnverifiedParentPanicRegression(t *testing.T) { } addSubnetBlk0 := vm.manager.NewBlock(statelessStandardBlk) - statelessStandardBlk, err = blocks.NewStandardBlock( + statelessStandardBlk, err = blocks.NewApricotStandardBlock( preferredID, preferredHeight+1, []*txs.Tx{addSubnetTx1}, @@ -434,7 +436,7 @@ func TestUnverifiedParentPanicRegression(t *testing.T) { } addSubnetBlk1 := vm.manager.NewBlock(statelessStandardBlk) - statelessStandardBlk, err = blocks.NewStandardBlock( + statelessStandardBlk, err = blocks.NewApricotStandardBlock( addSubnetBlk1.ID(), preferredHeight+2, []*txs.Tx{addSubnetTx2}, @@ -498,13 +500,13 @@ func TestRejectedStateRegressionInvalidValidatorTimestamp(t *testing.T) { require.NoError(err) // Create the proposal block to add the new validator - preferred, err := vm.Preferred() + preferred, err := vm.Builder.Preferred() require.NoError(err) preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessBlk, err := blocks.NewProposalBlock( + statelessBlk, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, addValidatorTx, @@ -578,7 +580,7 @@ func TestRejectedStateRegressionInvalidValidatorTimestamp(t *testing.T) { preferredID = addValidatorProposalCommit.ID() preferredHeight = addValidatorProposalCommit.Height() - statelessImportBlk, err := blocks.NewStandardBlock( + statelessImportBlk, err := blocks.NewApricotStandardBlock( preferredID, preferredHeight+1, []*txs.Tx{signedImportTx}, @@ -641,7 +643,7 @@ func TestRejectedStateRegressionInvalidValidatorTimestamp(t *testing.T) { preferredID = importBlk.ID() preferredHeight = importBlk.Height() - statelessAdvanceTimeProposalBlk, err := blocks.NewProposalBlock( + statelessAdvanceTimeProposalBlk, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, advanceTimeTx, @@ -661,7 +663,7 @@ func TestRejectedStateRegressionInvalidValidatorTimestamp(t *testing.T) { advanceTimeProposalCommitIntf := advanceTimeProposalOptions[0] advanceTimeProposalCommit, ok := advanceTimeProposalCommitIntf.(*blockexecutor.Block) require.True(ok) - _, ok = advanceTimeProposalCommit.Block.(*blocks.CommitBlock) + _, ok = advanceTimeProposalCommit.Block.(*blocks.ApricotCommitBlock) require.True(ok) err = advanceTimeProposalCommit.Verify() @@ -721,7 +723,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { vm.ctx.Lock.Unlock() }() - vm.state.SetCurrentSupply(defaultRewardConfig.SupplyCap / 2) + vm.state.SetCurrentSupply(constants.PrimaryNetworkID, defaultRewardConfig.SupplyCap/2) newValidatorStartTime0 := defaultGenesisTime.Add(txexecutor.SyncBound).Add(1 * time.Second) newValidatorEndTime0 := newValidatorStartTime0.Add(defaultMaxStakingDuration) @@ -742,13 +744,13 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { require.NoError(err) // Create the proposal block to add the first new validator - preferred, err := vm.Preferred() + preferred, err := vm.Builder.Preferred() require.NoError(err) preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessAddValidatorProposalBlk0, err := blocks.NewProposalBlock( + statelessAddValidatorProposalBlk0, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, addValidatorTx0, @@ -766,7 +768,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { addValidatorProposalCommitIntf0 := addValidatorProposalOptions0[0] addValidatorProposalCommit0, ok := addValidatorProposalCommitIntf0.(*blockexecutor.Block) require.True(ok) - _, ok = addValidatorProposalCommit0.Block.(*blocks.CommitBlock) + _, ok = addValidatorProposalCommit0.Block.(*blocks.ApricotCommitBlock) require.True(ok) err = addValidatorProposalCommit0.Verify() @@ -792,7 +794,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { preferredID = addValidatorProposalCommit0.ID() preferredHeight = addValidatorProposalCommit0.Height() - statelessAdvanceTimeProposalBlk0, err := blocks.NewProposalBlock( + statelessAdvanceTimeProposalBlk0, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, advanceTimeTx0, @@ -813,7 +815,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { advanceTimeProposalCommitIntf0 := advanceTimeProposalOptions0[0] advanceTimeProposalCommit0, ok := advanceTimeProposalCommitIntf0.(*blockexecutor.Block) require.True(ok) - _, ok = advanceTimeProposalCommit0.Block.(*blocks.CommitBlock) + _, ok = advanceTimeProposalCommit0.Block.(*blocks.ApricotCommitBlock) require.True(ok) err = advanceTimeProposalCommit0.Verify() @@ -876,7 +878,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { preferredID = advanceTimeProposalCommit0.ID() preferredHeight = advanceTimeProposalCommit0.Height() - statelessImportBlk, err := blocks.NewStandardBlock( + statelessImportBlk, err := blocks.NewApricotStandardBlock( preferredID, preferredHeight+1, []*txs.Tx{signedImportTx}, @@ -949,7 +951,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { preferredID = importBlk.ID() preferredHeight = importBlk.Height() - statelessAddValidatorProposalBlk1, err := blocks.NewProposalBlock( + statelessAddValidatorProposalBlk1, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, addValidatorTx1, @@ -968,7 +970,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { addValidatorProposalCommitIntf1 := addValidatorProposalOptions1[0] addValidatorProposalCommit1, ok := addValidatorProposalCommitIntf1.(*blockexecutor.Block) require.True(ok) - _, ok = addValidatorProposalCommit1.Block.(*blocks.CommitBlock) + _, ok = addValidatorProposalCommit1.Block.(*blocks.ApricotCommitBlock) require.True(ok) err = addValidatorProposalCommit1.Verify() @@ -994,7 +996,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { preferredID = addValidatorProposalCommit1.ID() preferredHeight = addValidatorProposalCommit1.Height() - statelessAdvanceTimeProposalBlk1, err := blocks.NewProposalBlock( + statelessAdvanceTimeProposalBlk1, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, advanceTimeTx1, @@ -1015,7 +1017,7 @@ func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { advanceTimeProposalCommitIntf1 := advanceTimeProposalOptions1[0] advanceTimeProposalCommit1, ok := advanceTimeProposalCommitIntf1.(*blockexecutor.Block) require.True(ok) - _, ok = advanceTimeProposalCommit1.Block.(*blocks.CommitBlock) + _, ok = advanceTimeProposalCommit1.Block.(*blocks.ApricotCommitBlock) require.True(ok) err = advanceTimeProposalCommit1.Verify() @@ -1143,13 +1145,13 @@ func TestValidatorSetAtCacheOverwriteRegression(t *testing.T) { require.NoError(err) // Create the proposal block to add the first new validator - preferred, err := vm.Preferred() + preferred, err := vm.Builder.Preferred() require.NoError(err) preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessProposalBlk, err := blocks.NewProposalBlock( + statelessProposalBlk, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, addValidatorTx0, @@ -1177,13 +1179,13 @@ func TestValidatorSetAtCacheOverwriteRegression(t *testing.T) { // Create the proposal block that moves the first new validator from the // pending validator set into the current validator set. - preferred, err = vm.Preferred() + preferred, err = vm.Builder.Preferred() require.NoError(err) preferredID = preferred.ID() preferredHeight = preferred.Height() - statelessProposalBlk, err = blocks.NewProposalBlock( + statelessProposalBlk, err = blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, advanceTimeTx0, @@ -1265,7 +1267,7 @@ func TestAddDelegatorTxAddBeforeRemove(t *testing.T) { require.NoError(err) // trigger block creation for the validator tx - addValidatorBlock, err := vm.BuildBlock() + addValidatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addValidatorBlock) @@ -1287,7 +1289,7 @@ func TestAddDelegatorTxAddBeforeRemove(t *testing.T) { require.NoError(err) // trigger block creation for the first add delegator tx - addFirstDelegatorBlock, err := vm.BuildBlock() + addFirstDelegatorBlock, err := vm.Builder.BuildBlock() require.NoError(err) verifyAndAcceptProposalCommitment(require, vm, addFirstDelegatorBlock) @@ -1320,11 +1322,11 @@ func verifyAndAcceptProposalCommitment(require *require.Assertions, vm *VM, blk // verify the preferences commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok, "expected commit block to be preferred") abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok, "expected abort block to be issued") // Verify the options diff --git a/avalanchego/vms/platformvm/vm_test.go b/avalanchego/vms/platformvm/vm_test.go index f5e629da..35bdcac8 100644 --- a/avalanchego/vms/platformvm/vm_test.go +++ b/avalanchego/vms/platformvm/vm_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package platformvm @@ -44,6 +44,7 @@ import ( "github.com/ava-labs/avalanchego/utils/math/meter" "github.com/ava-labs/avalanchego/utils/resource" "github.com/ava-labs/avalanchego/utils/timer" + "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/version" @@ -182,14 +183,14 @@ func defaultGenesis() (*api.BuildGenesisArgs, []byte) { } } - genesisValidators := make([]api.PrimaryValidator, len(keys)) + genesisValidators := make([]api.PermissionlessValidator, len(keys)) for i, key := range keys { nodeID := ids.NodeID(key.PublicKey().Address()) addr, err := address.FormatBech32(hrp, nodeID.Bytes()) if err != nil { panic(err) } - genesisValidators[i] = api.PrimaryValidator{ + genesisValidators[i] = api.PermissionlessValidator{ Staker: api.Staker{ StartTime: json.Uint64(defaultValidateStartTime.Unix()), EndTime: json.Uint64(defaultValidateEndTime.Unix()), @@ -257,14 +258,14 @@ func BuildGenesisTestWithArgs(t *testing.T, args *api.BuildGenesisArgs) (*api.Bu } } - genesisValidators := make([]api.PrimaryValidator, len(keys)) + genesisValidators := make([]api.PermissionlessValidator, len(keys)) for i, key := range keys { nodeID := ids.NodeID(key.PublicKey().Address()) addr, err := address.FormatBech32(hrp, nodeID.Bytes()) if err != nil { panic(err) } - genesisValidators[i] = api.PrimaryValidator{ + genesisValidators[i] = api.PermissionlessValidator{ Staker: api.Staker{ StartTime: json.Uint64(defaultValidateStartTime.Unix()), EndTime: json.Uint64(defaultValidateEndTime.Unix()), @@ -319,6 +320,7 @@ func defaultVM() (*VM, database.Database, *mutableSharedMemory) { Validators: validators.NewManager(), TxFee: defaultTxFee, CreateSubnetTxFee: 100 * defaultTxFee, + TransformSubnetTxFee: 100 * defaultTxFee, CreateBlockchainTxFee: 100 * defaultTxFee, MinValidatorStake: defaultMinValidatorStake, MaxValidatorStake: defaultMaxValidatorStake, @@ -328,6 +330,7 @@ func defaultVM() (*VM, database.Database, *mutableSharedMemory) { RewardConfig: defaultRewardConfig, ApricotPhase3Time: defaultValidateEndTime, ApricotPhase5Time: defaultValidateEndTime, + BanffTime: mockable.MaxTime, }, }} @@ -372,7 +375,7 @@ func defaultVM() (*VM, database.Database, *mutableSharedMemory) { panic(err) } else if err := vm.Builder.AddUnverifiedTx(testSubnet1); err != nil { panic(err) - } else if blk, err := vm.BuildBlock(); err != nil { + } else if blk, err := vm.Builder.BuildBlock(); err != nil { panic(err) } else if err := blk.Verify(); err != nil { panic(err) @@ -406,6 +409,7 @@ func GenesisVMWithArgs(t *testing.T, args *api.BuildGenesisArgs) ([]byte, chan c MinStakeDuration: defaultMinStakingDuration, MaxStakeDuration: defaultMaxStakingDuration, RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, }, }} @@ -446,7 +450,7 @@ func GenesisVMWithArgs(t *testing.T, args *api.BuildGenesisArgs) ([]byte, chan c panic(err) } else if err := vm.Builder.AddUnverifiedTx(testSubnet1); err != nil { panic(err) - } else if blk, err := vm.BuildBlock(); err != nil { + } else if blk, err := vm.Builder.BuildBlock(); err != nil { panic(err) } else if err := blk.Verify(); err != nil { panic(err) @@ -573,7 +577,7 @@ func TestAddValidatorCommit(t *testing.T) { // trigger block creation require.NoError(vm.Builder.AddUnverifiedTx(tx)) - blk, err := vm.BuildBlock() + blk, err := vm.Builder.BuildBlock() require.NoError(err) require.NoError(blk.Verify()) @@ -584,7 +588,7 @@ func TestAddValidatorCommit(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) require.NoError(block.Accept()) @@ -631,13 +635,13 @@ func TestInvalidAddValidatorCommit(t *testing.T) { t.Fatal(err) } - preferred, err := vm.Preferred() + preferred, err := vm.Builder.Preferred() if err != nil { t.Fatal(err) } preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessBlk, err := blocks.NewProposalBlock(preferredID, preferredHeight+1, tx) + statelessBlk, err := blocks.NewApricotProposalBlock(preferredID, preferredHeight+1, tx) if err != nil { t.Fatal(err) } @@ -655,7 +659,8 @@ func TestInvalidAddValidatorCommit(t *testing.T) { if err := parsedBlock.Verify(); err == nil { t.Fatalf("Should have errored during verification") } - if _, dropped := vm.Builder.GetDropReason(statelessBlk.Tx.ID()); !dropped { + txID := statelessBlk.Txs()[0].ID() + if _, dropped := vm.Builder.GetDropReason(txID); !dropped { t.Fatal("tx should be in dropped tx cache") } } @@ -691,7 +696,7 @@ func TestAddValidatorReject(t *testing.T) { // trigger block creation require.NoError(vm.Builder.AddUnverifiedTx(tx)) - blk, err := vm.BuildBlock() + blk, err := vm.Builder.BuildBlock() require.NoError(err) require.NoError(blk.Verify()) @@ -702,11 +707,11 @@ func TestAddValidatorReject(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -791,7 +796,7 @@ func TestAddSubnetValidatorAccept(t *testing.T) { // trigger block creation require.NoError(vm.Builder.AddUnverifiedTx(tx)) - blk, err := vm.BuildBlock() + blk, err := vm.Builder.BuildBlock() require.NoError(err) require.NoError(blk.Verify()) @@ -802,11 +807,11 @@ func TestAddSubnetValidatorAccept(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -864,7 +869,7 @@ func TestAddSubnetValidatorReject(t *testing.T) { // trigger block creation require.NoError(vm.Builder.AddUnverifiedTx(tx)) - blk, err := vm.BuildBlock() + blk, err := vm.Builder.BuildBlock() require.NoError(err) require.NoError(blk.Verify()) @@ -875,11 +880,11 @@ func TestAddSubnetValidatorReject(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -919,7 +924,7 @@ func TestRewardValidatorAccept(t *testing.T) { // Fast forward clock to time for genesis validators to leave vm.clock.Set(defaultValidateEndTime) - blk, err := vm.BuildBlock() // should contain proposal to advance time + blk, err := vm.Builder.BuildBlock() // should contain proposal to advance time require.NoError(err) require.NoError(blk.Verify()) @@ -930,10 +935,10 @@ func TestRewardValidatorAccept(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -963,7 +968,7 @@ func TestRewardValidatorAccept(t *testing.T) { timestamp := vm.state.GetTimestamp() require.Equal(defaultValidateEndTime.Unix(), timestamp.Unix()) - blk, err = vm.BuildBlock() // should contain proposal to reward genesis validator + blk, err = vm.Builder.BuildBlock() // should contain proposal to reward genesis validator require.NoError(err) require.NoError(blk.Verify()) @@ -974,11 +979,11 @@ func TestRewardValidatorAccept(t *testing.T) { require.NoError(err) commit = options[0].(*blockexecutor.Block) - _, ok = commit.Block.(*blocks.CommitBlock) + _, ok = commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort = options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -1018,7 +1023,7 @@ func TestRewardValidatorReject(t *testing.T) { // Fast forward clock to time for genesis validators to leave vm.clock.Set(defaultValidateEndTime) - blk, err := vm.BuildBlock() // should contain proposal to advance time + blk, err := vm.Builder.BuildBlock() // should contain proposal to advance time require.NoError(err) require.NoError(blk.Verify()) @@ -1028,11 +1033,11 @@ func TestRewardValidatorReject(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -1059,7 +1064,7 @@ func TestRewardValidatorReject(t *testing.T) { timestamp := vm.state.GetTimestamp() require.Equal(defaultValidateEndTime.Unix(), timestamp.Unix()) - blk, err = vm.BuildBlock() // should contain proposal to reward genesis validator + blk, err = vm.Builder.BuildBlock() // should contain proposal to reward genesis validator require.NoError(err) require.NoError(blk.Verify()) @@ -1069,11 +1074,11 @@ func TestRewardValidatorReject(t *testing.T) { require.NoError(err) commit = options[0].(*blockexecutor.Block) - _, ok = commit.Block.(*blocks.CommitBlock) + _, ok = commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort = options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(blk.Accept()) @@ -1113,7 +1118,7 @@ func TestRewardValidatorPreferred(t *testing.T) { // Fast forward clock to time for genesis validators to leave vm.clock.Set(defaultValidateEndTime) - blk, err := vm.BuildBlock() // should contain proposal to advance time + blk, err := vm.Builder.BuildBlock() // should contain proposal to advance time require.NoError(err) require.NoError(blk.Verify()) @@ -1123,11 +1128,11 @@ func TestRewardValidatorPreferred(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -1155,7 +1160,7 @@ func TestRewardValidatorPreferred(t *testing.T) { require.Equal(defaultValidateEndTime.Unix(), timestamp.Unix()) // should contain proposal to reward genesis validator - blk, err = vm.BuildBlock() + blk, err = vm.Builder.BuildBlock() require.NoError(err) require.NoError(blk.Verify()) @@ -1165,11 +1170,11 @@ func TestRewardValidatorPreferred(t *testing.T) { require.NoError(err) commit = options[0].(*blockexecutor.Block) - _, ok = commit.Block.(*blocks.CommitBlock) + _, ok = commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort = options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(blk.Accept()) @@ -1206,7 +1211,7 @@ func TestUnneededBuildBlock(t *testing.T) { } vm.ctx.Lock.Unlock() }() - if _, err := vm.BuildBlock(); err == nil { + if _, err := vm.Builder.BuildBlock(); err == nil { t.Fatalf("Should have errored on BuildBlock") } } @@ -1235,7 +1240,7 @@ func TestCreateChain(t *testing.T) { t.Fatal(err) } else if err := vm.Builder.AddUnverifiedTx(tx); err != nil { t.Fatal(err) - } else if blk, err := vm.BuildBlock(); err != nil { // should contain proposal to create chain + } else if blk, err := vm.Builder.BuildBlock(); err != nil { // should contain proposal to create chain t.Fatal(err) } else if err := blk.Verify(); err != nil { t.Fatal(err) @@ -1293,7 +1298,7 @@ func TestCreateSubnet(t *testing.T) { require.NoError(vm.Builder.AddUnverifiedTx(createSubnetTx)) // should contain proposal to create subnet - blk, err := vm.BuildBlock() + blk, err := vm.Builder.BuildBlock() require.NoError(err) require.NoError(blk.Verify()) @@ -1333,7 +1338,7 @@ func TestCreateSubnet(t *testing.T) { require.NoError(vm.Builder.AddUnverifiedTx(addValidatorTx)) - blk, err = vm.BuildBlock() // should add validator to the new subnet + blk, err = vm.Builder.BuildBlock() // should add validator to the new subnet require.NoError(err) require.NoError(blk.Verify()) @@ -1344,11 +1349,11 @@ func TestCreateSubnet(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -1379,7 +1384,7 @@ func TestCreateSubnet(t *testing.T) { // Create a block with an advance time tx that moves validator // from pending to current validator set vm.clock.Set(startTime) - blk, err = vm.BuildBlock() // should be advance time tx + blk, err = vm.Builder.BuildBlock() // should be advance time tx require.NoError(err) require.NoError(blk.Verify()) @@ -1390,11 +1395,11 @@ func TestCreateSubnet(t *testing.T) { require.NoError(err) commit = options[0].(*blockexecutor.Block) - _, ok = commit.Block.(*blocks.CommitBlock) + _, ok = commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort = options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -1426,7 +1431,7 @@ func TestCreateSubnet(t *testing.T) { // fast forward clock to time validator should stop validating vm.clock.Set(endTime) - blk, err = vm.BuildBlock() // should be advance time tx + blk, err = vm.Builder.BuildBlock() // should be advance time tx require.NoError(err) require.NoError(blk.Verify()) @@ -1438,11 +1443,11 @@ func TestCreateSubnet(t *testing.T) { require.NoError(err) commit = options[0].(*blockexecutor.Block) - _, ok = commit.Block.(*blocks.CommitBlock) + _, ok = commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort = options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -1544,7 +1549,7 @@ func TestAtomicImport(t *testing.T) { if err := vm.Builder.AddUnverifiedTx(tx); err != nil { t.Fatal(err) - } else if blk, err := vm.BuildBlock(); err != nil { + } else if blk, err := vm.Builder.BuildBlock(); err != nil { t.Fatal(err) } else if err := blk.Verify(); err != nil { t.Fatal(err) @@ -1593,14 +1598,14 @@ func TestOptimisticAtomicImport(t *testing.T) { t.Fatal(err) } - preferred, err := vm.Preferred() + preferred, err := vm.Builder.Preferred() if err != nil { t.Fatal(err) } preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessBlk, err := blocks.NewAtomicBlock( + statelessBlk, err := blocks.NewApricotAtomicBlock( preferredID, preferredHeight+1, tx, @@ -1654,6 +1659,7 @@ func TestRestartPartiallyAccepted(t *testing.T) { MinStakeDuration: defaultMinStakingDuration, MaxStakeDuration: defaultMaxStakingDuration, RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, }, }} firstVM.clock.Set(defaultGenesisTime) @@ -1675,14 +1681,14 @@ func TestRestartPartiallyAccepted(t *testing.T) { t.Fatal(err) } - preferred, err := firstVM.Preferred() + preferred, err := firstVM.Builder.Preferred() if err != nil { t.Fatal(err) } preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessBlk, err := blocks.NewProposalBlock( + statelessBlk, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, firstAdvanceTimeTx, @@ -1744,6 +1750,7 @@ func TestRestartPartiallyAccepted(t *testing.T) { MinStakeDuration: defaultMinStakingDuration, MaxStakeDuration: defaultMaxStakingDuration, RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, }, }} @@ -1786,6 +1793,7 @@ func TestRestartFullyAccepted(t *testing.T) { MinStakeDuration: defaultMinStakingDuration, MaxStakeDuration: defaultMaxStakingDuration, RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, }, }} @@ -1803,14 +1811,14 @@ func TestRestartFullyAccepted(t *testing.T) { t.Fatal(err) } - preferred, err := firstVM.Preferred() + preferred, err := firstVM.Builder.Preferred() if err != nil { t.Fatal(err) } preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessBlk, err := blocks.NewProposalBlock( + statelessBlk, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, firstAdvanceTimeTx, @@ -1870,6 +1878,7 @@ func TestRestartFullyAccepted(t *testing.T) { MinStakeDuration: defaultMinStakingDuration, MaxStakeDuration: defaultMaxStakingDuration, RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, }, }} @@ -1899,302 +1908,314 @@ func TestRestartFullyAccepted(t *testing.T) { // test bootstrapping the node func TestBootstrapPartiallyAccepted(t *testing.T) { - _, genesisBytes := defaultGenesis() + require := require.New(t) - baseDBManager := manager.NewMemDB(version.Semantic1_0_0) - vmDBManager := baseDBManager.NewPrefixDBManager([]byte("vm")) - bootstrappingDB := prefixdb.New([]byte("bootstrapping"), baseDBManager.Current().Database) + // TODO: add "useProto=true" once handler supports proto + for _, useProto := range []bool{false} { + t.Run(fmt.Sprintf("use proto buf message creator %v", useProto), func(tt *testing.T) { + _, genesisBytes := defaultGenesis() - blocked, err := queue.NewWithMissing(bootstrappingDB, "", prometheus.NewRegistry()) - if err != nil { - t.Fatal(err) - } + baseDBManager := manager.NewMemDB(version.Semantic1_0_0) + vmDBManager := baseDBManager.NewPrefixDBManager([]byte("vm")) + bootstrappingDB := prefixdb.New([]byte("bootstrapping"), baseDBManager.Current().Database) - vm := &VM{Factory: Factory{ - Config: config.Config{ - Chains: chains.MockManager{}, - Validators: validators.NewManager(), - UptimeLockedCalculator: uptime.NewLockedCalculator(), - MinStakeDuration: defaultMinStakingDuration, - MaxStakeDuration: defaultMaxStakingDuration, - RewardConfig: defaultRewardConfig, - }, - }} + blocked, err := queue.NewWithMissing(bootstrappingDB, "", prometheus.NewRegistry()) + require.NoError(err) - vm.clock.Set(defaultGenesisTime) - ctx := defaultContext() - consensusCtx := snow.DefaultConsensusContextTest() - consensusCtx.Context = ctx - consensusCtx.SetState(snow.Initializing) - ctx.Lock.Lock() + vm := &VM{Factory: Factory{ + Config: config.Config{ + Chains: chains.MockManager{}, + Validators: validators.NewManager(), + UptimeLockedCalculator: uptime.NewLockedCalculator(), + MinStakeDuration: defaultMinStakingDuration, + MaxStakeDuration: defaultMaxStakingDuration, + RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, + }, + }} + + vm.clock.Set(defaultGenesisTime) + ctx := defaultContext() + consensusCtx := snow.DefaultConsensusContextTest() + consensusCtx.Context = ctx + consensusCtx.SetState(snow.Initializing) + ctx.Lock.Lock() + + msgChan := make(chan common.Message, 1) + require.NoError(vm.Initialize(ctx, vmDBManager, genesisBytes, nil, nil, msgChan, nil, nil)) + + preferred, err := vm.Builder.Preferred() + require.NoError(err) - msgChan := make(chan common.Message, 1) - if err := vm.Initialize(ctx, vmDBManager, genesisBytes, nil, nil, msgChan, nil, nil); err != nil { - t.Fatal(err) - } + preferredID := preferred.ID() + preferredHeight := preferred.Height() - preferred, err := vm.Preferred() - if err != nil { - t.Fatal(err) - } - preferredID := preferred.ID() - preferredHeight := preferred.Height() + advanceTimeTx, err := vm.txBuilder.NewAdvanceTimeTx(defaultGenesisTime.Add(time.Second)) + require.NoError(err) - advanceTimeTx, err := vm.txBuilder.NewAdvanceTimeTx(defaultGenesisTime.Add(time.Second)) - if err != nil { - t.Fatal(err) - } - statelessBlk, err := blocks.NewProposalBlock( - preferredID, - preferredHeight+1, - advanceTimeTx, - ) - if err != nil { - t.Fatal(err) - } - advanceTimeBlk := vm.manager.NewBlock(statelessBlk) - if err != nil { - t.Fatal(err) - } - advanceTimeBlkID := advanceTimeBlk.ID() - advanceTimeBlkBytes := advanceTimeBlk.Bytes() + statelessBlk, err := blocks.NewApricotProposalBlock( + preferredID, + preferredHeight+1, + advanceTimeTx, + ) + require.NoError(err) - peerID := ids.NodeID{1, 2, 3, 4, 5, 4, 3, 2, 1} - vdrs := validators.NewSet() - if err := vdrs.AddWeight(peerID, 1); err != nil { - t.Fatal(err) - } - beacons := vdrs + advanceTimeBlk := vm.manager.NewBlock(statelessBlk) + require.NoError(err) - benchlist := benchlist.NewNoBenchlist() - timeoutManager, err := timeout.NewManager( - &timer.AdaptiveTimeoutConfig{ - InitialTimeout: time.Millisecond, - MinimumTimeout: time.Millisecond, - MaximumTimeout: 10 * time.Second, - TimeoutHalflife: 5 * time.Minute, - TimeoutCoefficient: 1.25, - }, - benchlist, - "", - prometheus.NewRegistry(), - ) - if err != nil { - t.Fatal(err) - } - go timeoutManager.Dispatch() - - chainRouter := &router.ChainRouter{} - metrics := prometheus.NewRegistry() - mc, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) - require.NoError(t, err) - err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, timeoutManager, time.Second, ids.Set{}, ids.Set{}, nil, router.HealthConfig{}, "", prometheus.NewRegistry()) - require.NoError(t, err) - - externalSender := &sender.ExternalSenderTest{TB: t} - externalSender.Default(true) - - // Passes messages from the consensus engine to the network - sender, err := sender.New( - consensusCtx, - mc, - externalSender, - chainRouter, - timeoutManager, - sender.GossipConfig{ - AcceptedFrontierPeerSize: 1, - OnAcceptPeerSize: 1, - AppGossipValidatorSize: 1, - AppGossipNonValidatorSize: 1, - }, - ) - require.NoError(t, err) - - var reqID uint32 - externalSender.SendF = func(msg message.OutboundMessage, nodeIDs ids.NodeIDSet, _ ids.ID, _ bool) ids.NodeIDSet { - inMsg, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) - require.NoError(t, err) - require.Equal(t, message.GetAcceptedFrontier, inMsg.Op()) - - res := nodeIDs - requestID, ok := inMsg.Get(message.RequestID).(uint32) - require.True(t, ok) - - reqID = requestID - return res - } - - isBootstrapped := false - subnet := &common.SubnetTest{ - T: t, - IsBootstrappedF: func() bool { return isBootstrapped }, - BootstrappedF: func(ids.ID) { isBootstrapped = true }, - } - - peers := tracker.NewPeers() - startup := tracker.NewStartup(peers, (beacons.Weight()+1)/2) - beacons.RegisterCallbackListener(startup) - - // The engine handles consensus - consensus := &smcon.Topological{} - commonCfg := common.Config{ - Ctx: consensusCtx, - Validators: vdrs, - Beacons: beacons, - SampleK: beacons.Len(), - StartupTracker: startup, - Alpha: (beacons.Weight() + 1) / 2, - Sender: sender, - Subnet: subnet, - AncestorsMaxContainersSent: 2000, - AncestorsMaxContainersReceived: 2000, - SharedCfg: &common.SharedConfig{}, - } - - snowGetHandler, err := snowgetter.New(vm, commonCfg) - require.NoError(t, err) - - bootstrapConfig := bootstrap.Config{ - Config: commonCfg, - AllGetsServer: snowGetHandler, - Blocked: blocked, - VM: vm, - } - - // Asynchronously passes messages from the network to the consensus engine - cpuTracker, err := timetracker.NewResourceTracker(prometheus.NewRegistry(), resource.NoUsage, meter.ContinuousFactory{}, time.Second) - require.NoError(t, err) - handler, err := handler.New( - mc, - bootstrapConfig.Ctx, - vdrs, - msgChan, - nil, - time.Hour, - cpuTracker, - ) - require.NoError(t, err) - - engineConfig := smeng.Config{ - Ctx: bootstrapConfig.Ctx, - AllGetsServer: snowGetHandler, - VM: bootstrapConfig.VM, - Sender: bootstrapConfig.Sender, - Validators: vdrs, - Params: snowball.Parameters{ - K: 1, - Alpha: 1, - BetaVirtuous: 20, - BetaRogue: 20, - ConcurrentRepolls: 1, - OptimalProcessing: 1, - MaxOutstandingItems: 1, - MaxItemProcessingTime: 1, - }, - Consensus: consensus, - } - engine, err := smeng.New(engineConfig) - if err != nil { - t.Fatal(err) - } - handler.SetConsensus(engine) + advanceTimeBlkID := advanceTimeBlk.ID() + advanceTimeBlkBytes := advanceTimeBlk.Bytes() + + peerID := ids.NodeID{1, 2, 3, 4, 5, 4, 3, 2, 1} + vdrs := validators.NewSet() + require.NoError(vdrs.AddWeight(peerID, 1)) + beacons := vdrs + + benchlist := benchlist.NewNoBenchlist() + timeoutManager, err := timeout.NewManager( + &timer.AdaptiveTimeoutConfig{ + InitialTimeout: time.Millisecond, + MinimumTimeout: time.Millisecond, + MaximumTimeout: 10 * time.Second, + TimeoutHalflife: 5 * time.Minute, + TimeoutCoefficient: 1.25, + }, + benchlist, + "", + prometheus.NewRegistry(), + ) + require.NoError(err) - bootstrapper, err := bootstrap.New( - bootstrapConfig, - engine.Start, - ) - if err != nil { - t.Fatal(err) - } - handler.SetBootstrapper(bootstrapper) + go timeoutManager.Dispatch() - // Allow incoming messages to be routed to the new chain - chainRouter.AddChain(handler) - ctx.Lock.Unlock() + chainRouter := &router.ChainRouter{} - handler.Start(false) + metrics := prometheus.NewRegistry() + mc, err := message.NewCreator(metrics, "dummyNamespace", true, 10*time.Second) + require.NoError(err) + mcProto, err := message.NewCreatorWithProto(metrics, "dummyNamespace", true, 10*time.Second) + require.NoError(err) - ctx.Lock.Lock() - if err := bootstrapper.Connected(peerID, version.CurrentApp); err != nil { - t.Fatal(err) - } + err = chainRouter.Initialize(ids.EmptyNodeID, logging.NoLog{}, mc, timeoutManager, time.Second, ids.Set{}, ids.Set{}, nil, router.HealthConfig{}, "", prometheus.NewRegistry()) + require.NoError(err) - externalSender.SendF = func(msg message.OutboundMessage, nodeIDs ids.NodeIDSet, _ ids.ID, _ bool) ids.NodeIDSet { - inMsg, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) - require.NoError(t, err) - require.Equal(t, message.GetAccepted, inMsg.Op()) + externalSender := &sender.ExternalSenderTest{TB: t} + externalSender.Default(true) + + // Passes messages from the consensus engine to the network + sender, err := sender.New( + consensusCtx, + mc, + mcProto, + time.Now().Add(time.Hour), + externalSender, + chainRouter, + timeoutManager, + sender.GossipConfig{ + AcceptedFrontierPeerSize: 1, + OnAcceptPeerSize: 1, + AppGossipValidatorSize: 1, + AppGossipNonValidatorSize: 1, + }, + ) + require.NoError(err) - res := nodeIDs - requestID, ok := inMsg.Get(message.RequestID).(uint32) - require.True(t, ok) + var reqID uint32 + externalSender.SendF = func(msg message.OutboundMessage, nodeIDs ids.NodeIDSet, _ ids.ID, _ bool) ids.NodeIDSet { + var inMsg message.InboundMessage + if !useProto { + inMsg, err = mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) + } else { + inMsg, err = mcProto.Parse(msg.Bytes(), ctx.NodeID, func() {}) + } + require.NoError(err) + require.Equal(message.GetAcceptedFrontier, inMsg.Op()) - reqID = requestID - return res - } + requestIDIntf, err := inMsg.Get(message.RequestID) + require.NoError(err) + requestID, ok := requestIDIntf.(uint32) + require.True(ok) - frontier := []ids.ID{advanceTimeBlkID} - if err := bootstrapper.AcceptedFrontier(peerID, reqID, frontier); err != nil { - t.Fatal(err) - } + reqID = requestID + return nodeIDs + } - externalSender.SendF = func(msg message.OutboundMessage, nodeIDs ids.NodeIDSet, _ ids.ID, _ bool) ids.NodeIDSet { - inMsg, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) - require.NoError(t, err) - require.Equal(t, message.GetAncestors, inMsg.Op()) + isBootstrapped := false + subnet := &common.SubnetTest{ + T: tt, + IsBootstrappedF: func() bool { return isBootstrapped }, + BootstrappedF: func(ids.ID) { isBootstrapped = true }, + } - res := nodeIDs - requestID, ok := inMsg.Get(message.RequestID).(uint32) - require.True(t, ok) - reqID = requestID + peers := tracker.NewPeers() + startup := tracker.NewStartup(peers, (beacons.Weight()+1)/2) + beacons.RegisterCallbackListener(startup) + + // The engine handles consensus + consensus := &smcon.Topological{} + commonCfg := common.Config{ + Ctx: consensusCtx, + Validators: vdrs, + Beacons: beacons, + SampleK: beacons.Len(), + StartupTracker: startup, + Alpha: (beacons.Weight() + 1) / 2, + Sender: sender, + Subnet: subnet, + AncestorsMaxContainersSent: 2000, + AncestorsMaxContainersReceived: 2000, + SharedCfg: &common.SharedConfig{}, + } - containerID, err := ids.ToID(inMsg.Get(message.ContainerID).([]byte)) - require.NoError(t, err) - if containerID != advanceTimeBlkID { - t.Fatalf("wrong block requested") - } + snowGetHandler, err := snowgetter.New(vm, commonCfg) + require.NoError(err) - return res - } + bootstrapConfig := bootstrap.Config{ + Config: commonCfg, + AllGetsServer: snowGetHandler, + Blocked: blocked, + VM: vm, + } - if err := bootstrapper.Accepted(peerID, reqID, frontier); err != nil { - t.Fatal(err) - } + // Asynchronously passes messages from the network to the consensus engine + cpuTracker, err := timetracker.NewResourceTracker(prometheus.NewRegistry(), resource.NoUsage, meter.ContinuousFactory{}, time.Second) + require.NoError(err) - externalSender.SendF = nil - externalSender.CantSend = false + handler, err := handler.New( + mc, + bootstrapConfig.Ctx, + vdrs, + msgChan, + nil, + time.Hour, + cpuTracker, + ) + require.NoError(err) - if err := bootstrapper.Ancestors(peerID, reqID, [][]byte{advanceTimeBlkBytes}); err != nil { - t.Fatal(err) - } + engineConfig := smeng.Config{ + Ctx: bootstrapConfig.Ctx, + AllGetsServer: snowGetHandler, + VM: bootstrapConfig.VM, + Sender: bootstrapConfig.Sender, + Validators: vdrs, + Params: snowball.Parameters{ + K: 1, + Alpha: 1, + BetaVirtuous: 20, + BetaRogue: 20, + ConcurrentRepolls: 1, + OptimalProcessing: 1, + MaxOutstandingItems: 1, + MaxItemProcessingTime: 1, + }, + Consensus: consensus, + } + engine, err := smeng.New(engineConfig) + require.NoError(err) - preferred, err = vm.Preferred() - if err != nil { - t.Fatal(err) - } + handler.SetConsensus(engine) - options, err := advanceTimeBlk.(smcon.OracleBlock).Options() - if err != nil { - t.Fatal(err) - } + bootstrapper, err := bootstrap.New( + bootstrapConfig, + engine.Start, + ) + require.NoError(err) - // Because the block needs to have been verified for it's preference to be - // set correctly, we manually select the correct preference here. - advanceTimePreference := options[0] + handler.SetBootstrapper(bootstrapper) - if preferred.ID() != advanceTimePreference.ID() { - t.Fatalf("wrong preference reported after bootstrapping to proposal block\nPreferred: %s\nExpected: %s\nGenesis: %s", - preferred.ID(), - advanceTimePreference.ID(), - preferredID) - } - ctx.Lock.Unlock() + // Allow incoming messages to be routed to the new chain + chainRouter.AddChain(handler) + ctx.Lock.Unlock() + + handler.Start(false) + + ctx.Lock.Lock() + if err := bootstrapper.Connected(peerID, version.CurrentApp); err != nil { + t.Fatal(err) + } + + externalSender.SendF = func(msg message.OutboundMessage, nodeIDs ids.NodeIDSet, _ ids.ID, _ bool) ids.NodeIDSet { + inMsg, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) + require.NoError(err) + require.Equal(message.GetAccepted, inMsg.Op()) + + requestIDIntf, err := inMsg.Get(message.RequestID) + require.NoError(err) + + requestID, ok := requestIDIntf.(uint32) + require.True(ok) + + reqID = requestID + return nodeIDs + } + + frontier := []ids.ID{advanceTimeBlkID} + if err := bootstrapper.AcceptedFrontier(peerID, reqID, frontier); err != nil { + t.Fatal(err) + } + + externalSender.SendF = func(msg message.OutboundMessage, nodeIDs ids.NodeIDSet, _ ids.ID, _ bool) ids.NodeIDSet { + var inMsg message.InboundMessage + if !useProto { + inMsg, err = mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) + } else { + inMsg, err = mcProto.Parse(msg.Bytes(), ctx.NodeID, func() {}) + } + require.NoError(err) + require.Equal(message.GetAncestors, inMsg.Op()) + + requestIDIntf, err := inMsg.Get(message.RequestID) + require.NoError(err) + requestID, ok := requestIDIntf.(uint32) + require.True(ok) + + reqID = requestID + + containerIDIntf, err := inMsg.Get(message.ContainerID) + require.NoError(err) + containerIDBytes, ok := containerIDIntf.([]byte) + require.True(ok) + containerID, err := ids.ToID(containerIDBytes) + require.NoError(err) + if containerID != advanceTimeBlkID { + t.Fatalf("wrong block requested") + } + + return nodeIDs + } + + require.NoError(bootstrapper.Accepted(peerID, reqID, frontier)) + + externalSender.SendF = nil + externalSender.CantSend = false + + require.NoError(bootstrapper.Ancestors(peerID, reqID, [][]byte{advanceTimeBlkBytes})) + + preferred, err = vm.Builder.Preferred() + require.NoError(err) - chainRouter.Shutdown() + options, err := advanceTimeBlk.(smcon.OracleBlock).Options() + require.NoError(err) + + // Because the block needs to have been verified for it's preference to be + // set correctly, we manually select the correct preference here. + advanceTimePreference := options[0] + + if preferred.ID() != advanceTimePreference.ID() { + tt.Fatalf("wrong preference reported after bootstrapping to proposal block\nPreferred: %s\nExpected: %s\nGenesis: %s", + preferred.ID(), + advanceTimePreference.ID(), + preferredID) + } + ctx.Lock.Unlock() + + chainRouter.Shutdown() + }) + } } func TestUnverifiedParent(t *testing.T) { _, genesisBytes := defaultGenesis() - dbManager := manager.NewMemDB(version.Semantic1_0_0) vm := &VM{Factory: Factory{ @@ -2205,6 +2226,7 @@ func TestUnverifiedParent(t *testing.T) { MinStakeDuration: defaultMinStakingDuration, MaxStakeDuration: defaultMaxStakingDuration, RewardConfig: defaultRewardConfig, + BanffTime: mockable.MaxTime, }, }} @@ -2228,14 +2250,14 @@ func TestUnverifiedParent(t *testing.T) { t.Fatal(err) } - preferred, err := vm.Preferred() + preferred, err := vm.Builder.Preferred() if err != nil { t.Fatal(err) } preferredID := preferred.ID() preferredHeight := preferred.Height() - statelessBlk, err := blocks.NewProposalBlock( + statelessBlk, err := blocks.NewApricotProposalBlock( preferredID, preferredHeight+1, firstAdvanceTimeTx, @@ -2261,7 +2283,7 @@ func TestUnverifiedParent(t *testing.T) { if err != nil { t.Fatal(err) } - statelessSecondAdvanceTimeBlk, err := blocks.NewProposalBlock( + statelessSecondAdvanceTimeBlk, err := blocks.NewApricotProposalBlock( firstOption.ID(), firstOption.(*blockexecutor.Block).Height()+1, secondAdvanceTimeTx, @@ -2348,6 +2370,7 @@ func TestUptimeDisallowedWithRestart(t *testing.T) { RewardConfig: defaultRewardConfig, Validators: validators.NewManager(), UptimeLockedCalculator: uptime.NewLockedCalculator(), + BanffTime: mockable.MaxTime, }, }} @@ -2376,6 +2399,7 @@ func TestUptimeDisallowedWithRestart(t *testing.T) { UptimePercentage: .21, Validators: validators.NewManager(), UptimeLockedCalculator: uptime.NewLockedCalculator(), + BanffTime: mockable.MaxTime, }, }} @@ -2398,7 +2422,7 @@ func TestUptimeDisallowedWithRestart(t *testing.T) { secondVM.clock.Set(defaultValidateEndTime) secondVM.uptimeManager.(uptime.TestManager).SetTime(defaultValidateEndTime) - blk, err := secondVM.BuildBlock() // should contain proposal to advance time + blk, err := secondVM.Builder.BuildBlock() // should contain proposal to advance time require.NoError(err) require.NoError(blk.Verify()) @@ -2409,11 +2433,11 @@ func TestUptimeDisallowedWithRestart(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -2442,7 +2466,7 @@ func TestUptimeDisallowedWithRestart(t *testing.T) { timestamp := secondVM.state.GetTimestamp() require.Equal(defaultValidateEndTime.Unix(), timestamp.Unix()) - blk, err = secondVM.BuildBlock() // should contain proposal to reward genesis validator + blk, err = secondVM.Builder.BuildBlock() // should contain proposal to reward genesis validator require.NoError(err) require.NoError(blk.Verify()) @@ -2452,11 +2476,11 @@ func TestUptimeDisallowedWithRestart(t *testing.T) { require.NoError(err) commit = options[1].(*blockexecutor.Block) - _, ok = commit.Block.(*blocks.CommitBlock) + _, ok = commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort = options[0].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(blk.Accept()) @@ -2500,6 +2524,7 @@ func TestUptimeDisallowedAfterNeverConnecting(t *testing.T) { RewardConfig: defaultRewardConfig, Validators: validators.NewManager(), UptimeLockedCalculator: uptime.NewLockedCalculator(), + BanffTime: mockable.MaxTime, }, }} @@ -2524,7 +2549,7 @@ func TestUptimeDisallowedAfterNeverConnecting(t *testing.T) { vm.clock.Set(defaultValidateEndTime) vm.uptimeManager.(uptime.TestManager).SetTime(defaultValidateEndTime) - blk, err := vm.BuildBlock() // should contain proposal to advance time + blk, err := vm.Builder.BuildBlock() // should contain proposal to advance time require.NoError(err) require.NoError(blk.Verify()) @@ -2535,11 +2560,11 @@ func TestUptimeDisallowedAfterNeverConnecting(t *testing.T) { require.NoError(err) commit := options[0].(*blockexecutor.Block) - _, ok := commit.Block.(*blocks.CommitBlock) + _, ok := commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) abort := options[1].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) require.NoError(block.Accept()) @@ -2553,7 +2578,7 @@ func TestUptimeDisallowedAfterNeverConnecting(t *testing.T) { require.Equal(defaultValidateEndTime.Unix(), timestamp.Unix()) // should contain proposal to reward genesis validator - blk, err = vm.BuildBlock() + blk, err = vm.Builder.BuildBlock() require.NoError(err) require.NoError(blk.Verify()) @@ -2563,11 +2588,11 @@ func TestUptimeDisallowedAfterNeverConnecting(t *testing.T) { require.NoError(err) abort = options[0].(*blockexecutor.Block) - _, ok = abort.Block.(*blocks.AbortBlock) + _, ok = abort.Block.(*blocks.ApricotAbortBlock) require.True(ok) commit = options[1].(*blockexecutor.Block) - _, ok = commit.Block.(*blocks.CommitBlock) + _, ok = commit.Block.(*blocks.ApricotCommitBlock) require.True(ok) require.NoError(blk.Accept()) diff --git a/avalanchego/vms/propertyfx/burn_operation.go b/avalanchego/vms/propertyfx/burn_operation.go index 600f3c6e..bb111812 100644 --- a/avalanchego/vms/propertyfx/burn_operation.go +++ b/avalanchego/vms/propertyfx/burn_operation.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/burn_operation_test.go b/avalanchego/vms/propertyfx/burn_operation_test.go index dcbbb748..65748c83 100644 --- a/avalanchego/vms/propertyfx/burn_operation_test.go +++ b/avalanchego/vms/propertyfx/burn_operation_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/credential.go b/avalanchego/vms/propertyfx/credential.go index 007237eb..564bcd73 100644 --- a/avalanchego/vms/propertyfx/credential.go +++ b/avalanchego/vms/propertyfx/credential.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/credential_test.go b/avalanchego/vms/propertyfx/credential_test.go index 0b6185d9..f469049f 100644 --- a/avalanchego/vms/propertyfx/credential_test.go +++ b/avalanchego/vms/propertyfx/credential_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/factory.go b/avalanchego/vms/propertyfx/factory.go index 306559a2..8aeecba2 100644 --- a/avalanchego/vms/propertyfx/factory.go +++ b/avalanchego/vms/propertyfx/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/factory_test.go b/avalanchego/vms/propertyfx/factory_test.go index 8d89c241..788e7124 100644 --- a/avalanchego/vms/propertyfx/factory_test.go +++ b/avalanchego/vms/propertyfx/factory_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/fx.go b/avalanchego/vms/propertyfx/fx.go index 27cf64f2..78e4abec 100644 --- a/avalanchego/vms/propertyfx/fx.go +++ b/avalanchego/vms/propertyfx/fx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/fx_test.go b/avalanchego/vms/propertyfx/fx_test.go index e1748655..e7645873 100644 --- a/avalanchego/vms/propertyfx/fx_test.go +++ b/avalanchego/vms/propertyfx/fx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/mint_operation.go b/avalanchego/vms/propertyfx/mint_operation.go index 520fb581..088cc7d1 100644 --- a/avalanchego/vms/propertyfx/mint_operation.go +++ b/avalanchego/vms/propertyfx/mint_operation.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/mint_operation_test.go b/avalanchego/vms/propertyfx/mint_operation_test.go index ec585085..b616660d 100644 --- a/avalanchego/vms/propertyfx/mint_operation_test.go +++ b/avalanchego/vms/propertyfx/mint_operation_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/mint_output.go b/avalanchego/vms/propertyfx/mint_output.go index 4ca557a5..7f84d3df 100644 --- a/avalanchego/vms/propertyfx/mint_output.go +++ b/avalanchego/vms/propertyfx/mint_output.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/mint_output_test.go b/avalanchego/vms/propertyfx/mint_output_test.go index b7858d2d..f17ba6fb 100644 --- a/avalanchego/vms/propertyfx/mint_output_test.go +++ b/avalanchego/vms/propertyfx/mint_output_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/owned_output.go b/avalanchego/vms/propertyfx/owned_output.go index edad8d55..dba2f26c 100644 --- a/avalanchego/vms/propertyfx/owned_output.go +++ b/avalanchego/vms/propertyfx/owned_output.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/propertyfx/owned_output_test.go b/avalanchego/vms/propertyfx/owned_output_test.go index 1ebcaa50..36e59981 100644 --- a/avalanchego/vms/propertyfx/owned_output_test.go +++ b/avalanchego/vms/propertyfx/owned_output_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package propertyfx diff --git a/avalanchego/vms/proposervm/batched_vm.go b/avalanchego/vms/proposervm/batched_vm.go index 0d21de4f..932a9871 100644 --- a/avalanchego/vms/proposervm/batched_vm.go +++ b/avalanchego/vms/proposervm/batched_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -92,14 +92,19 @@ func (vm *VM) BatchedParseBlock(blks [][]byte) ([]snowman.Block, error) { innerBlocksIndex int statelessBlockDescs = make([]partialData, 0, len(blks)) innerBlockBytes = make([][]byte, 0, len(blks)) + banffActivated = vm.Clock.Time().After(vm.activationTimeBanff) ) for ; blocksIndex < len(blks); blocksIndex++ { blkBytes := blks[blocksIndex] - statelessBlock, err := statelessblock.Parse(blkBytes) + statelessBlock, requireBanff, err := statelessblock.Parse(blkBytes) if err != nil { break } + if requireBanff && !banffActivated { + break + } + blkID := statelessBlock.ID() block, exists := vm.verifiedBlocks[blkID] if exists { diff --git a/avalanchego/vms/proposervm/batched_vm_test.go b/avalanchego/vms/proposervm/batched_vm_test.go index 12b0eaf2..5aa0a5d6 100644 --- a/avalanchego/vms/proposervm/batched_vm_test.go +++ b/avalanchego/vms/proposervm/batched_vm_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -891,7 +891,7 @@ func initTestRemoteProposerVM( } } - proVM := New(coreVM, proBlkStartTime, 0) + proVM := New(coreVM, proBlkStartTime, 0, time.Time{}) valState := &validators.TestState{ T: t, diff --git a/avalanchego/vms/proposervm/block.go b/avalanchego/vms/proposervm/block.go index 0ee66990..754081cd 100644 --- a/avalanchego/vms/proposervm/block.go +++ b/avalanchego/vms/proposervm/block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -211,28 +211,51 @@ func (p *postForkCommonComponents) buildChild( return nil, err } + banffActivated := newTimestamp.After(p.vm.activationTimeBanff) + // Build the child var statelessChild block.SignedBlock if delay >= proposer.MaxDelay { - statelessChild, err = block.BuildUnsigned( - parentID, - newTimestamp, - pChainHeight, - innerBlock.Bytes(), - ) + if banffActivated { + statelessChild, err = block.BuildUnsignedBanff( + parentID, + newTimestamp, + pChainHeight, + innerBlock.Bytes(), + ) + } else { + statelessChild, err = block.BuildUnsignedApricot( + parentID, + newTimestamp, + pChainHeight, + innerBlock.Bytes(), + ) + } if err != nil { return nil, err } } else { - statelessChild, err = block.Build( - parentID, - newTimestamp, - pChainHeight, - p.vm.ctx.StakingCertLeaf, - innerBlock.Bytes(), - p.vm.ctx.ChainID, - p.vm.ctx.StakingLeafSigner, - ) + if banffActivated { + statelessChild, err = block.BuildBanff( + parentID, + newTimestamp, + pChainHeight, + p.vm.ctx.StakingCertLeaf, + innerBlock.Bytes(), + p.vm.ctx.ChainID, + p.vm.ctx.StakingLeafSigner, + ) + } else { + statelessChild, err = block.BuildApricot( + parentID, + newTimestamp, + pChainHeight, + p.vm.ctx.StakingCertLeaf, + innerBlock.Bytes(), + p.vm.ctx.ChainID, + p.vm.ctx.StakingLeafSigner, + ) + } if err != nil { return nil, err } @@ -249,6 +272,8 @@ func (p *postForkCommonComponents) buildChild( p.vm.ctx.Log.Info("built block", zap.Stringer("blkID", child.ID()), + zap.Stringer("innerBlkID", innerBlock.ID()), + zap.Uint64("height", child.Height()), zap.Time("parentTimestamp", parentTimestamp), zap.Time("blockTimestamp", newTimestamp), ) diff --git a/avalanchego/vms/proposervm/block/block.go b/avalanchego/vms/proposervm/block/block.go index 400cdd25..1967abc3 100644 --- a/avalanchego/vms/proposervm/block/block.go +++ b/avalanchego/vms/proposervm/block/block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/vms/proposervm/block/block_test.go b/avalanchego/vms/proposervm/block/block_test.go index 32f9d82c..afa68b6f 100644 --- a/avalanchego/vms/proposervm/block/block_test.go +++ b/avalanchego/vms/proposervm/block/block_test.go @@ -1,15 +1,18 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block import ( + "bytes" "testing" "time" "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/codec/reflectcodec" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/units" ) func equal(require *require.Assertions, chainID ids.ID, want, have SignedBlock) { @@ -32,7 +35,7 @@ func TestVerifyNoCertWithSignature(t *testing.T) { require := require.New(t) - builtBlockIntf, err := BuildUnsigned(parentID, timestamp, pChainHeight, innerBlockBytes) + builtBlockIntf, err := BuildUnsignedApricot(parentID, timestamp, pChainHeight, innerBlockBytes) require.NoError(err) builtBlock := builtBlockIntf.(*statelessBlock) @@ -44,3 +47,20 @@ func TestVerifyNoCertWithSignature(t *testing.T) { err = builtBlock.Verify(true, ids.Empty) require.Error(err) } + +func TestBlockSizeLimitExceeded(t *testing.T) { + require := require.New(t) + + parentID := ids.ID{1} + timestamp := time.Unix(123, 0) + pChainHeight := uint64(2) + innerBlockBytes := bytes.Repeat([]byte{0}, 270*units.KiB) + + // linear codec should fail to marshal, due to exceeded maximum length + _, err := BuildUnsignedApricot(parentID, timestamp, pChainHeight, innerBlockBytes) + require.ErrorIs(err, reflectcodec.ErrMaxMarshalSliceLimitExceeded) + + // with the new max limit, it should be able to build blocks + _, err = BuildUnsignedBanff(parentID, timestamp, pChainHeight, innerBlockBytes) + require.NoError(err) +} diff --git a/avalanchego/vms/proposervm/block/build.go b/avalanchego/vms/proposervm/block/build.go index 51d4c681..1da65904 100644 --- a/avalanchego/vms/proposervm/block/build.go +++ b/avalanchego/vms/proposervm/block/build.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block @@ -9,12 +9,32 @@ import ( "crypto/x509" "time" + "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/wrappers" ) -func BuildUnsigned( +func BuildUnsignedApricot( + parentID ids.ID, + timestamp time.Time, + pChainHeight uint64, + blockBytes []byte, +) (SignedBlock, error) { + return buildUnsigned(apricotCodec, parentID, timestamp, pChainHeight, blockBytes) +} + +func BuildUnsignedBanff( + parentID ids.ID, + timestamp time.Time, + pChainHeight uint64, + blockBytes []byte, +) (SignedBlock, error) { + return buildUnsigned(banffCodec, parentID, timestamp, pChainHeight, blockBytes) +} + +func buildUnsigned( + cm codec.Manager, parentID ids.ID, timestamp time.Time, pChainHeight uint64, @@ -31,14 +51,39 @@ func BuildUnsigned( timestamp: timestamp, } - bytes, err := c.Marshal(version, &block) + bytes, err := cm.Marshal(codecVersion, &block) if err != nil { return nil, err } return block, block.initialize(bytes) } -func Build( +func BuildApricot( + parentID ids.ID, + timestamp time.Time, + pChainHeight uint64, + cert *x509.Certificate, + blockBytes []byte, + chainID ids.ID, + key crypto.Signer, +) (SignedBlock, error) { + return build(apricotCodec, parentID, timestamp, pChainHeight, cert, blockBytes, chainID, key) +} + +func BuildBanff( + parentID ids.ID, + timestamp time.Time, + pChainHeight uint64, + cert *x509.Certificate, + blockBytes []byte, + chainID ids.ID, + key crypto.Signer, +) (SignedBlock, error) { + return build(banffCodec, parentID, timestamp, pChainHeight, cert, blockBytes, chainID, key) +} + +func build( + cm codec.Manager, parentID ids.ID, timestamp time.Time, pChainHeight uint64, @@ -61,7 +106,7 @@ func Build( } var blockIntf SignedBlock = block - unsignedBytesWithEmptySignature, err := c.Marshal(version, &blockIntf) + unsignedBytesWithEmptySignature, err := cm.Marshal(codecVersion, &blockIntf) if err != nil { return nil, err } @@ -85,7 +130,7 @@ func Build( return nil, err } - block.bytes, err = c.Marshal(version, &blockIntf) + block.bytes, err = cm.Marshal(codecVersion, &blockIntf) return block, err } @@ -100,7 +145,7 @@ func BuildHeader( Body: bodyID, } - bytes, err := c.Marshal(version, &header) + bytes, err := banffCodec.Marshal(codecVersion, &header) header.bytes = bytes return &header, err } @@ -117,7 +162,7 @@ func BuildOption( InnerBytes: innerBytes, } - bytes, err := c.Marshal(version, &block) + bytes, err := banffCodec.Marshal(codecVersion, &block) if err != nil { return nil, err } diff --git a/avalanchego/vms/proposervm/block/build_test.go b/avalanchego/vms/proposervm/block/build_test.go index ab600f28..3b8e10eb 100644 --- a/avalanchego/vms/proposervm/block/build_test.go +++ b/avalanchego/vms/proposervm/block/build_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block @@ -29,7 +29,7 @@ func TestBuild(t *testing.T) { cert := tlsCert.Leaf key := tlsCert.PrivateKey.(crypto.Signer) - builtBlock, err := Build( + builtBlock, err := BuildApricot( parentID, timestamp, pChainHeight, @@ -60,7 +60,7 @@ func TestBuildUnsigned(t *testing.T) { require := require.New(t) - builtBlock, err := BuildUnsigned(parentID, timestamp, pChainHeight, innerBlockBytes) + builtBlock, err := BuildUnsignedApricot(parentID, timestamp, pChainHeight, innerBlockBytes) require.NoError(err) require.Equal(parentID, builtBlock.ParentID()) diff --git a/avalanchego/vms/proposervm/block/codec.go b/avalanchego/vms/proposervm/block/codec.go index 9e038472..09b8a915 100644 --- a/avalanchego/vms/proposervm/block/codec.go +++ b/avalanchego/vms/proposervm/block/codec.go @@ -1,28 +1,51 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block import ( + "math" + "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" "github.com/ava-labs/avalanchego/utils/wrappers" ) -const version = 0 +const codecVersion = 0 + +var ( + // TODO: After Banff is activated, this codec can be deleted. + // + // Before the Banff upgrade, enforce a 256 KiB maximum block size. + apricotCodec codec.Manager -var c codec.Manager + // After the Banff upgrade, the maximum block size is enforced by the + // p2p message size limit. See: [constants.DefaultMaxMessageSize] + // + // Invariant: This codec must never be used to unmarshal a slice unless it + // is a `[]byte`. Otherwise a malicious payload could cause an + // OOM. + banffCodec codec.Manager +) func init() { - lc := linearcodec.NewDefault() - c = codec.NewDefaultManager() + // codec.defaultMaxSize of 256 KiB + apricotLinearCodec := linearcodec.NewDefault() + apricotCodec = codec.NewDefaultManager() + + // maximum allowable size + banffLinearCodec := linearcodec.NewCustomMaxLength(math.MaxUint32) + banffCodec = codec.NewManager(math.MaxInt) errs := wrappers.Errs{} errs.Add( - lc.RegisterType(&statelessBlock{}), - lc.RegisterType(&option{}), + apricotLinearCodec.RegisterType(&statelessBlock{}), + apricotLinearCodec.RegisterType(&option{}), + apricotCodec.RegisterCodec(codecVersion, apricotLinearCodec), - c.RegisterCodec(version, lc), + banffLinearCodec.RegisterType(&statelessBlock{}), + banffLinearCodec.RegisterType(&option{}), + banffCodec.RegisterCodec(codecVersion, banffLinearCodec), ) if errs.Errored() { panic(errs.Err) diff --git a/avalanchego/vms/proposervm/block/header.go b/avalanchego/vms/proposervm/block/header.go index e8c093fd..591c8509 100644 --- a/avalanchego/vms/proposervm/block/header.go +++ b/avalanchego/vms/proposervm/block/header.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/vms/proposervm/block/header_test.go b/avalanchego/vms/proposervm/block/header_test.go index efa2e4d5..3e89327a 100644 --- a/avalanchego/vms/proposervm/block/header_test.go +++ b/avalanchego/vms/proposervm/block/header_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/vms/proposervm/block/option.go b/avalanchego/vms/proposervm/block/option.go index 49632fd9..79eee19f 100644 --- a/avalanchego/vms/proposervm/block/option.go +++ b/avalanchego/vms/proposervm/block/option.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/vms/proposervm/block/option_test.go b/avalanchego/vms/proposervm/block/option_test.go index f8b92084..c721439c 100644 --- a/avalanchego/vms/proposervm/block/option_test.go +++ b/avalanchego/vms/proposervm/block/option_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block diff --git a/avalanchego/vms/proposervm/block/parse.go b/avalanchego/vms/proposervm/block/parse.go index 1b9bbfa7..01a61a9a 100644 --- a/avalanchego/vms/proposervm/block/parse.go +++ b/avalanchego/vms/proposervm/block/parse.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block @@ -7,26 +7,33 @@ import ( "fmt" ) -func Parse(bytes []byte) (Block, error) { - var block Block - parsedVersion, err := c.Unmarshal(bytes, &block) +func Parse(bytes []byte) (Block, bool, error) { + var ( + block Block + requireBanff bool + ) + parsedVersion, err := apricotCodec.Unmarshal(bytes, &block) if err != nil { - return nil, err + parsedVersion, err = banffCodec.Unmarshal(bytes, &block) + requireBanff = true + } + if err != nil { + return nil, false, err } - if parsedVersion != version { - return nil, fmt.Errorf("expected codec version %d but got %d", version, parsedVersion) + if parsedVersion != codecVersion { + return nil, false, fmt.Errorf("expected codec version %d but got %d", codecVersion, parsedVersion) } - return block, block.initialize(bytes) + return block, requireBanff, block.initialize(bytes) } func ParseHeader(bytes []byte) (Header, error) { header := statelessHeader{} - parsedVersion, err := c.Unmarshal(bytes, &header) + parsedVersion, err := banffCodec.Unmarshal(bytes, &header) if err != nil { return nil, err } - if parsedVersion != version { - return nil, fmt.Errorf("expected codec version %d but got %d", version, parsedVersion) + if parsedVersion != codecVersion { + return nil, fmt.Errorf("expected codec version %d but got %d", codecVersion, parsedVersion) } header.bytes = bytes return &header, nil diff --git a/avalanchego/vms/proposervm/block/parse_test.go b/avalanchego/vms/proposervm/block/parse_test.go index 5b362b73..40e4e72c 100644 --- a/avalanchego/vms/proposervm/block/parse_test.go +++ b/avalanchego/vms/proposervm/block/parse_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package block @@ -30,7 +30,7 @@ func TestParse(t *testing.T) { cert := tlsCert.Leaf key := tlsCert.PrivateKey.(crypto.Signer) - builtBlock, err := Build( + builtBlock, err := BuildApricot( parentID, timestamp, pChainHeight, @@ -43,8 +43,9 @@ func TestParse(t *testing.T) { builtBlockBytes := builtBlock.Bytes() - parsedBlockIntf, err := Parse(builtBlockBytes) + parsedBlockIntf, requireBanff, err := Parse(builtBlockBytes) require.NoError(err) + require.False(requireBanff) parsedBlock, ok := parsedBlockIntf.(SignedBlock) require.True(ok) @@ -59,7 +60,7 @@ func TestParseDuplicateExtension(t *testing.T) { blockBytes, err := hex.DecodeString(blockHex) require.NoError(err) - _, err = Parse(blockBytes) + _, _, err = Parse(blockBytes) require.Error(err) // Do not check for errDuplicateExtension to support g1.19 } @@ -96,8 +97,9 @@ func TestParseOption(t *testing.T) { builtOptionBytes := builtOption.Bytes() - parsedOption, err := Parse(builtOptionBytes) + parsedOption, requireBanff, err := Parse(builtOptionBytes) require.NoError(err) + require.False(requireBanff) equalOption(require, builtOption, parsedOption) } @@ -110,13 +112,14 @@ func TestParseUnsigned(t *testing.T) { pChainHeight := uint64(2) innerBlockBytes := []byte{3} - builtBlock, err := BuildUnsigned(parentID, timestamp, pChainHeight, innerBlockBytes) + builtBlock, err := BuildUnsignedApricot(parentID, timestamp, pChainHeight, innerBlockBytes) require.NoError(err) builtBlockBytes := builtBlock.Bytes() - parsedBlockIntf, err := Parse(builtBlockBytes) + parsedBlockIntf, requireBanff, err := Parse(builtBlockBytes) require.NoError(err) + require.False(requireBanff) parsedBlock, ok := parsedBlockIntf.(SignedBlock) require.True(ok) @@ -129,6 +132,6 @@ func TestParseGibberish(t *testing.T) { bytes := []byte{0, 1, 2, 3, 4, 5} - _, err := Parse(bytes) + _, _, err := Parse(bytes) require.Error(err) } diff --git a/avalanchego/vms/proposervm/block_server.go b/avalanchego/vms/proposervm/block_server.go index e164353c..d45676d6 100644 --- a/avalanchego/vms/proposervm/block_server.go +++ b/avalanchego/vms/proposervm/block_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm diff --git a/avalanchego/vms/proposervm/height_indexed_vm.go b/avalanchego/vms/proposervm/height_indexed_vm.go index ef885889..f92c31a9 100644 --- a/avalanchego/vms/proposervm/height_indexed_vm.go +++ b/avalanchego/vms/proposervm/height_indexed_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm diff --git a/avalanchego/vms/proposervm/indexer/block_server.go b/avalanchego/vms/proposervm/indexer/block_server.go index 5db41298..7ae79923 100644 --- a/avalanchego/vms/proposervm/indexer/block_server.go +++ b/avalanchego/vms/proposervm/indexer/block_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/vms/proposervm/indexer/block_server_test.go b/avalanchego/vms/proposervm/indexer/block_server_test.go index 52f6c1fc..caabac05 100644 --- a/avalanchego/vms/proposervm/indexer/block_server_test.go +++ b/avalanchego/vms/proposervm/indexer/block_server_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/vms/proposervm/indexer/height_indexer.go b/avalanchego/vms/proposervm/indexer/height_indexer.go index de0079b9..27f4b587 100644 --- a/avalanchego/vms/proposervm/indexer/height_indexer.go +++ b/avalanchego/vms/proposervm/indexer/height_indexer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer diff --git a/avalanchego/vms/proposervm/indexer/height_indexer_test.go b/avalanchego/vms/proposervm/indexer/height_indexer_test.go index 765368d8..ae144de0 100644 --- a/avalanchego/vms/proposervm/indexer/height_indexer_test.go +++ b/avalanchego/vms/proposervm/indexer/height_indexer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package indexer @@ -42,7 +42,7 @@ func TestHeightBlockIndexPostFork(t *testing.T) { dummyPCH := uint64(2022) // store postForkStatelessBlk in State ... - postForkStatelessBlk, err := block.BuildUnsigned( + postForkStatelessBlk, err := block.BuildUnsignedApricot( lastBlkID, dummyTS, dummyPCH, @@ -120,7 +120,7 @@ func TestHeightBlockIndexAcrossFork(t *testing.T) { dummyPCH := uint64(2022) // store postForkStatelessBlk in State ... - postForkStatelessBlk, err := block.BuildUnsigned( + postForkStatelessBlk, err := block.BuildUnsignedApricot( lastBlkID, dummyTS, dummyPCH, @@ -202,7 +202,7 @@ func TestHeightBlockIndexResumeFromCheckPoint(t *testing.T) { dummyPCH := uint64(2022) // store postForkStatelessBlk in State ... - postForkStatelessBlk, err := block.BuildUnsigned( + postForkStatelessBlk, err := block.BuildUnsignedApricot( lastBlkID, dummyTS, dummyPCH, diff --git a/avalanchego/vms/proposervm/post_fork_block.go b/avalanchego/vms/proposervm/post_fork_block.go index a47a9e55..2705fc7d 100644 --- a/avalanchego/vms/proposervm/post_fork_block.go +++ b/avalanchego/vms/proposervm/post_fork_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm diff --git a/avalanchego/vms/proposervm/post_fork_block_test.go b/avalanchego/vms/proposervm/post_fork_block_test.go index 3a4e8bf5..1091803b 100644 --- a/avalanchego/vms/proposervm/post_fork_block_test.go +++ b/avalanchego/vms/proposervm/post_fork_block_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -57,7 +57,7 @@ func TestOracle_PostForkBlock_ImplementsInterface(t *testing.T) { }, } - slb, err := block.Build( + slb, err := block.BuildApricot( ids.Empty, // refer unknown parent time.Time{}, 0, // pChainHeight, @@ -142,7 +142,7 @@ func TestBlockVerify_PostForkBlock_ParentChecks(t *testing.T) { BytesV: []byte{2}, TimestampV: prntCoreBlk.Timestamp(), } - childSlb, err := block.Build( + childSlb, err := block.BuildApricot( ids.Empty, // refer unknown parent childCoreBlk.Timestamp(), pChainHeight, @@ -170,7 +170,7 @@ func TestBlockVerify_PostForkBlock_ParentChecks(t *testing.T) { } // child block referring known parent does verify - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( prntProBlk.ID(), // refer known parent prntProBlk.Timestamp().Add(proposer.MaxDelay), pChainHeight, @@ -253,7 +253,7 @@ func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) { // child block timestamp cannot be lower than parent timestamp childCoreBlk.TimestampV = prntTimestamp.Add(-1 * time.Second) proVM.Clock.Set(childCoreBlk.TimestampV) - childSlb, err := block.Build( + childSlb, err := block.BuildApricot( prntProBlk.ID(), childCoreBlk.Timestamp(), pChainHeight, @@ -286,7 +286,7 @@ func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) { } beforeWinStart := prntTimestamp.Add(blkWinDelay).Add(-1 * time.Second) proVM.Clock.Set(beforeWinStart) - childSlb, err = block.Build( + childSlb, err = block.BuildApricot( prntProBlk.ID(), beforeWinStart, pChainHeight, @@ -307,7 +307,7 @@ func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) { // block can arrive at its creator window starts atWindowStart := prntTimestamp.Add(blkWinDelay) proVM.Clock.Set(atWindowStart) - childSlb, err = block.Build( + childSlb, err = block.BuildApricot( prntProBlk.ID(), atWindowStart, pChainHeight, @@ -328,7 +328,7 @@ func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) { // block can arrive after its creator window starts afterWindowStart := prntTimestamp.Add(blkWinDelay).Add(5 * time.Second) proVM.Clock.Set(afterWindowStart) - childSlb, err = block.Build( + childSlb, err = block.BuildApricot( prntProBlk.ID(), afterWindowStart, pChainHeight, @@ -348,7 +348,7 @@ func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) { // block can arrive within submission window AtSubWindowEnd := proVM.Time().Add(proposer.MaxDelay) proVM.Clock.Set(AtSubWindowEnd) - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( prntProBlk.ID(), AtSubWindowEnd, pChainHeight, @@ -364,7 +364,7 @@ func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) { // block timestamp cannot be too much in the future afterSubWinEnd := proVM.Time().Add(maxSkew).Add(time.Second) - childSlb, err = block.Build( + childSlb, err = block.BuildApricot( prntProBlk.ID(), afterSubWinEnd, pChainHeight, @@ -446,7 +446,7 @@ func TestBlockVerify_PostForkBlock_PChainHeightChecks(t *testing.T) { } // child P-Chain height must not precede parent P-Chain height - childSlb, err := block.Build( + childSlb, err := block.BuildApricot( prntProBlk.ID(), childCoreBlk.Timestamp(), prntBlkPChainHeight-1, @@ -474,7 +474,7 @@ func TestBlockVerify_PostForkBlock_PChainHeightChecks(t *testing.T) { } // child P-Chain height can be equal to parent P-Chain height - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( prntProBlk.ID(), childCoreBlk.Timestamp(), prntBlkPChainHeight, @@ -492,7 +492,7 @@ func TestBlockVerify_PostForkBlock_PChainHeightChecks(t *testing.T) { // child P-Chain height may follow parent P-Chain height pChainHeight = prntBlkPChainHeight * 2 // move ahead pChainHeight - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( prntProBlk.ID(), childCoreBlk.Timestamp(), prntBlkPChainHeight+1, @@ -508,7 +508,7 @@ func TestBlockVerify_PostForkBlock_PChainHeightChecks(t *testing.T) { // block P-Chain height can be equal to current P-Chain height currPChainHeight, _ := proVM.ctx.ValidatorState.GetCurrentHeight() - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( prntProBlk.ID(), childCoreBlk.Timestamp(), currPChainHeight, @@ -523,7 +523,7 @@ func TestBlockVerify_PostForkBlock_PChainHeightChecks(t *testing.T) { } // block P-Chain height cannot be at higher than current P-Chain height - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( prntProBlk.ID(), childCoreBlk.Timestamp(), currPChainHeight*2, @@ -650,7 +650,7 @@ func TestBlockVerify_PostForkBlockBuiltOnOption_PChainHeightChecks(t *testing.T) } // child P-Chain height must not precede parent P-Chain height - childSlb, err := block.Build( + childSlb, err := block.BuildApricot( parentBlk.ID(), childCoreBlk.Timestamp(), prntBlkPChainHeight-1, @@ -676,7 +676,7 @@ func TestBlockVerify_PostForkBlockBuiltOnOption_PChainHeightChecks(t *testing.T) } // child P-Chain height can be equal to parent P-Chain height - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( parentBlk.ID(), childCoreBlk.Timestamp(), prntBlkPChainHeight, @@ -694,7 +694,7 @@ func TestBlockVerify_PostForkBlockBuiltOnOption_PChainHeightChecks(t *testing.T) // child P-Chain height may follow parent P-Chain height pChainHeight = prntBlkPChainHeight * 2 // move ahead pChainHeight - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( parentBlk.ID(), childCoreBlk.Timestamp(), prntBlkPChainHeight+1, @@ -710,7 +710,7 @@ func TestBlockVerify_PostForkBlockBuiltOnOption_PChainHeightChecks(t *testing.T) // block P-Chain height can be equal to current P-Chain height currPChainHeight, _ := proVM.ctx.ValidatorState.GetCurrentHeight() - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( parentBlk.ID(), childCoreBlk.Timestamp(), currPChainHeight, @@ -725,7 +725,7 @@ func TestBlockVerify_PostForkBlockBuiltOnOption_PChainHeightChecks(t *testing.T) } // block P-Chain height cannot be at higher than current P-Chain height - childSlb, err = block.BuildUnsigned( + childSlb, err = block.BuildUnsignedApricot( parentBlk.ID(), childCoreBlk.Timestamp(), currPChainHeight*2, @@ -1050,7 +1050,7 @@ func TestBlockVerify_PostForkBlock_ShouldBePostForkOption(t *testing.T) { } // Build the child - statelessChild, err := block.Build( + statelessChild, err := block.BuildApricot( postForkOracleBlk.ID(), postForkOracleBlk.Timestamp().Add(proposer.WindowDuration), postForkOracleBlk.PChainHeight(), @@ -1110,7 +1110,7 @@ func TestBlockVerify_PostForkBlock_PChainTooLow(t *testing.T) { } } - statelessChild, err := block.BuildUnsigned( + statelessChild, err := block.BuildUnsignedApricot( coreGenBlk.ID(), coreGenBlk.Timestamp(), 4, diff --git a/avalanchego/vms/proposervm/post_fork_option.go b/avalanchego/vms/proposervm/post_fork_option.go index f5896f7a..8243afb8 100644 --- a/avalanchego/vms/proposervm/post_fork_option.go +++ b/avalanchego/vms/proposervm/post_fork_option.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm diff --git a/avalanchego/vms/proposervm/post_fork_option_test.go b/avalanchego/vms/proposervm/post_fork_option_test.go index 876567d1..a54f896b 100644 --- a/avalanchego/vms/proposervm/post_fork_option_test.go +++ b/avalanchego/vms/proposervm/post_fork_option_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -638,7 +638,7 @@ func TestOptionTimestampValidity(t *testing.T) { }, }, } - statelessBlock, err := block.BuildUnsigned( + statelessBlock, err := block.BuildUnsignedApricot( coreGenBlk.ID(), coreGenBlk.Timestamp(), 0, @@ -726,7 +726,7 @@ func TestOptionTimestampValidity(t *testing.T) { // Restart the node. ctx := proVM.ctx - proVM = New(coreVM, time.Time{}, 0) + proVM = New(coreVM, time.Time{}, 0, time.Time{}) coreVM.InitializeF = func( *snow.Context, diff --git a/avalanchego/vms/proposervm/pre_fork_block.go b/avalanchego/vms/proposervm/pre_fork_block.go index e358ae71..3fb5fdcf 100644 --- a/avalanchego/vms/proposervm/pre_fork_block.go +++ b/avalanchego/vms/proposervm/pre_fork_block.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -170,6 +170,7 @@ func (b *preForkBlock) buildChild() (Block, error) { b.vm.ctx.Log.Info("built block", zap.Stringer("blkID", innerBlock.ID()), + zap.Uint64("height", innerBlock.Height()), zap.Time("parentTimestamp", parentTimestamp), ) @@ -199,7 +200,7 @@ func (b *preForkBlock) buildChild() (Block, error) { return nil, err } - statelessBlock, err := block.BuildUnsigned( + statelessBlock, err := block.BuildUnsignedApricot( parentID, newTimestamp, pChainHeight, @@ -220,6 +221,8 @@ func (b *preForkBlock) buildChild() (Block, error) { b.vm.ctx.Log.Info("built block", zap.Stringer("blkID", blk.ID()), + zap.Stringer("innerBlkID", innerBlock.ID()), + zap.Uint64("height", blk.Height()), zap.Time("parentTimestamp", parentTimestamp), zap.Time("blockTimestamp", newTimestamp)) return blk, nil diff --git a/avalanchego/vms/proposervm/pre_fork_block_test.go b/avalanchego/vms/proposervm/pre_fork_block_test.go index f594fba1..f0610d4b 100644 --- a/avalanchego/vms/proposervm/pre_fork_block_test.go +++ b/avalanchego/vms/proposervm/pre_fork_block_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -341,7 +341,7 @@ func TestBlockVerify_BlocksBuiltOnPreForkGenesis(t *testing.T) { } // postFork block does NOT verify if parent is before fork activation time - postForkStatelessChild, err := block.Build( + postForkStatelessChild, err := block.BuildApricot( coreGenBlk.ID(), coreBlk.Timestamp(), 0, // pChainHeight @@ -762,7 +762,7 @@ func TestBlockVerify_ForkBlockIsOracleBlockButChildrenAreSigned(t *testing.T) { t.Fatal(err) } - slb, err := block.Build( + slb, err := block.BuildApricot( firstBlock.ID(), // refer unknown parent firstBlock.Timestamp(), 0, // pChainHeight, diff --git a/avalanchego/vms/proposervm/proposer/validators.go b/avalanchego/vms/proposervm/proposer/validators.go index 15741bf8..e5b29234 100644 --- a/avalanchego/vms/proposervm/proposer/validators.go +++ b/avalanchego/vms/proposervm/proposer/validators.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposer diff --git a/avalanchego/vms/proposervm/proposer/windower.go b/avalanchego/vms/proposervm/proposer/windower.go index d6179dc6..36255d21 100644 --- a/avalanchego/vms/proposervm/proposer/windower.go +++ b/avalanchego/vms/proposervm/proposer/windower.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposer diff --git a/avalanchego/vms/proposervm/proposer/windower_test.go b/avalanchego/vms/proposervm/proposer/windower_test.go index 82363fd9..fd5cd3a5 100644 --- a/avalanchego/vms/proposervm/proposer/windower_test.go +++ b/avalanchego/vms/proposervm/proposer/windower_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposer diff --git a/avalanchego/vms/proposervm/scheduler/scheduler.go b/avalanchego/vms/proposervm/scheduler/scheduler.go index 12895796..9a709d52 100644 --- a/avalanchego/vms/proposervm/scheduler/scheduler.go +++ b/avalanchego/vms/proposervm/scheduler/scheduler.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package scheduler @@ -81,7 +81,7 @@ waitloop: // from the VM to avoid deadlock s.log.Debug("dropping message from VM", zap.String("reason", "channel to engine is full"), - zap.Stringer("message", msg), + zap.Stringer("messageString", msg), ) } case buildBlockTime, ok := <-s.newBuildBlockTime: diff --git a/avalanchego/vms/proposervm/scheduler/scheduler_test.go b/avalanchego/vms/proposervm/scheduler/scheduler_test.go index edf80fb9..aeaeca1b 100644 --- a/avalanchego/vms/proposervm/scheduler/scheduler_test.go +++ b/avalanchego/vms/proposervm/scheduler/scheduler_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package scheduler diff --git a/avalanchego/vms/proposervm/state/block_height_index.go b/avalanchego/vms/proposervm/state/block_height_index.go index cb971e2f..f94d56aa 100644 --- a/avalanchego/vms/proposervm/state/block_height_index.go +++ b/avalanchego/vms/proposervm/state/block_height_index.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/proposervm/state/block_height_index_test.go b/avalanchego/vms/proposervm/state/block_height_index_test.go index be51a275..d7bbc37f 100644 --- a/avalanchego/vms/proposervm/state/block_height_index_test.go +++ b/avalanchego/vms/proposervm/state/block_height_index_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/proposervm/state/block_state.go b/avalanchego/vms/proposervm/state/block_state.go index c8d6939c..7cbde441 100644 --- a/avalanchego/vms/proposervm/state/block_state.go +++ b/avalanchego/vms/proposervm/state/block_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -17,9 +17,7 @@ import ( "github.com/ava-labs/avalanchego/vms/proposervm/block" ) -const ( - blockCacheSize = 8192 -) +const blockCacheSize = 8192 var ( errBlockWrongVersion = errors.New("wrong version") @@ -98,7 +96,7 @@ func (s *blockState) GetBlock(blkID ids.ID) (block.Block, choices.Status, error) } // The key was in the database - blk, err := block.Parse(blkWrapper.Block) + blk, _, err := block.Parse(blkWrapper.Block) if err != nil { return nil, choices.Unknown, err } diff --git a/avalanchego/vms/proposervm/state/block_state_test.go b/avalanchego/vms/proposervm/state/block_state_test.go index 0be49c3c..44c6a3c3 100644 --- a/avalanchego/vms/proposervm/state/block_state_test.go +++ b/avalanchego/vms/proposervm/state/block_state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state @@ -33,7 +33,7 @@ func testBlockState(a *require.Assertions, bs BlockState) { cert := tlsCert.Leaf key := tlsCert.PrivateKey.(crypto.Signer) - b, err := block.Build( + b, err := block.BuildApricot( parentID, timestamp, pChainHeight, diff --git a/avalanchego/vms/proposervm/state/chain_state.go b/avalanchego/vms/proposervm/state/chain_state.go index 422bdf83..c5311250 100644 --- a/avalanchego/vms/proposervm/state/chain_state.go +++ b/avalanchego/vms/proposervm/state/chain_state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/proposervm/state/chain_state_test.go b/avalanchego/vms/proposervm/state/chain_state_test.go index c0b935d9..b1ae6192 100644 --- a/avalanchego/vms/proposervm/state/chain_state_test.go +++ b/avalanchego/vms/proposervm/state/chain_state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/proposervm/state/codec.go b/avalanchego/vms/proposervm/state/codec.go index 5e1a45fe..c3b2692e 100644 --- a/avalanchego/vms/proposervm/state/codec.go +++ b/avalanchego/vms/proposervm/state/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/proposervm/state/mock_state.go b/avalanchego/vms/proposervm/state/mock_state.go new file mode 100644 index 00000000..cbf99bbc --- /dev/null +++ b/avalanchego/vms/proposervm/state/mock_state.go @@ -0,0 +1,285 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/vms/proposervm/state (interfaces: State) + +// Package state is a generated GoMock package. +package state + +import ( + reflect "reflect" + + versiondb "github.com/ava-labs/avalanchego/database/versiondb" + ids "github.com/ava-labs/avalanchego/ids" + choices "github.com/ava-labs/avalanchego/snow/choices" + logging "github.com/ava-labs/avalanchego/utils/logging" + block "github.com/ava-labs/avalanchego/vms/proposervm/block" + gomock "github.com/golang/mock/gomock" +) + +// MockState is a mock of State interface. +type MockState struct { + ctrl *gomock.Controller + recorder *MockStateMockRecorder +} + +// MockStateMockRecorder is the mock recorder for MockState. +type MockStateMockRecorder struct { + mock *MockState +} + +// NewMockState creates a new mock instance. +func NewMockState(ctrl *gomock.Controller) *MockState { + mock := &MockState{ctrl: ctrl} + mock.recorder = &MockStateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockState) EXPECT() *MockStateMockRecorder { + return m.recorder +} + +// Commit mocks base method. +func (m *MockState) Commit() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Commit") + ret0, _ := ret[0].(error) + return ret0 +} + +// Commit indicates an expected call of Commit. +func (mr *MockStateMockRecorder) Commit() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockState)(nil).Commit)) +} + +// DeleteCheckpoint mocks base method. +func (m *MockState) DeleteCheckpoint() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCheckpoint") + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCheckpoint indicates an expected call of DeleteCheckpoint. +func (mr *MockStateMockRecorder) DeleteCheckpoint() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCheckpoint", reflect.TypeOf((*MockState)(nil).DeleteCheckpoint)) +} + +// DeleteLastAccepted mocks base method. +func (m *MockState) DeleteLastAccepted() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteLastAccepted") + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteLastAccepted indicates an expected call of DeleteLastAccepted. +func (mr *MockStateMockRecorder) DeleteLastAccepted() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLastAccepted", reflect.TypeOf((*MockState)(nil).DeleteLastAccepted)) +} + +// GetBlock mocks base method. +func (m *MockState) GetBlock(arg0 ids.ID) (block.Block, choices.Status, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlock", arg0) + ret0, _ := ret[0].(block.Block) + ret1, _ := ret[1].(choices.Status) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetBlock indicates an expected call of GetBlock. +func (mr *MockStateMockRecorder) GetBlock(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlock", reflect.TypeOf((*MockState)(nil).GetBlock), arg0) +} + +// GetBlockIDAtHeight mocks base method. +func (m *MockState) GetBlockIDAtHeight(arg0 uint64) (ids.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlockIDAtHeight", arg0) + ret0, _ := ret[0].(ids.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBlockIDAtHeight indicates an expected call of GetBlockIDAtHeight. +func (mr *MockStateMockRecorder) GetBlockIDAtHeight(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockIDAtHeight", reflect.TypeOf((*MockState)(nil).GetBlockIDAtHeight), arg0) +} + +// GetCheckpoint mocks base method. +func (m *MockState) GetCheckpoint() (ids.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCheckpoint") + ret0, _ := ret[0].(ids.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCheckpoint indicates an expected call of GetCheckpoint. +func (mr *MockStateMockRecorder) GetCheckpoint() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCheckpoint", reflect.TypeOf((*MockState)(nil).GetCheckpoint)) +} + +// GetForkHeight mocks base method. +func (m *MockState) GetForkHeight() (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetForkHeight") + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetForkHeight indicates an expected call of GetForkHeight. +func (mr *MockStateMockRecorder) GetForkHeight() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetForkHeight", reflect.TypeOf((*MockState)(nil).GetForkHeight)) +} + +// GetLastAccepted mocks base method. +func (m *MockState) GetLastAccepted() (ids.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLastAccepted") + ret0, _ := ret[0].(ids.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLastAccepted indicates an expected call of GetLastAccepted. +func (mr *MockStateMockRecorder) GetLastAccepted() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastAccepted", reflect.TypeOf((*MockState)(nil).GetLastAccepted)) +} + +// HasIndexReset mocks base method. +func (m *MockState) HasIndexReset() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasIndexReset") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HasIndexReset indicates an expected call of HasIndexReset. +func (mr *MockStateMockRecorder) HasIndexReset() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasIndexReset", reflect.TypeOf((*MockState)(nil).HasIndexReset)) +} + +// IsIndexEmpty mocks base method. +func (m *MockState) IsIndexEmpty() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsIndexEmpty") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsIndexEmpty indicates an expected call of IsIndexEmpty. +func (mr *MockStateMockRecorder) IsIndexEmpty() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsIndexEmpty", reflect.TypeOf((*MockState)(nil).IsIndexEmpty)) +} + +// PutBlock mocks base method. +func (m *MockState) PutBlock(arg0 block.Block, arg1 choices.Status) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PutBlock", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// PutBlock indicates an expected call of PutBlock. +func (mr *MockStateMockRecorder) PutBlock(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutBlock", reflect.TypeOf((*MockState)(nil).PutBlock), arg0, arg1) +} + +// ResetHeightIndex mocks base method. +func (m *MockState) ResetHeightIndex(arg0 logging.Logger, arg1 versiondb.Commitable) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResetHeightIndex", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// ResetHeightIndex indicates an expected call of ResetHeightIndex. +func (mr *MockStateMockRecorder) ResetHeightIndex(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetHeightIndex", reflect.TypeOf((*MockState)(nil).ResetHeightIndex), arg0, arg1) +} + +// SetBlockIDAtHeight mocks base method. +func (m *MockState) SetBlockIDAtHeight(arg0 uint64, arg1 ids.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetBlockIDAtHeight", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetBlockIDAtHeight indicates an expected call of SetBlockIDAtHeight. +func (mr *MockStateMockRecorder) SetBlockIDAtHeight(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBlockIDAtHeight", reflect.TypeOf((*MockState)(nil).SetBlockIDAtHeight), arg0, arg1) +} + +// SetCheckpoint mocks base method. +func (m *MockState) SetCheckpoint(arg0 ids.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetCheckpoint", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetCheckpoint indicates an expected call of SetCheckpoint. +func (mr *MockStateMockRecorder) SetCheckpoint(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCheckpoint", reflect.TypeOf((*MockState)(nil).SetCheckpoint), arg0) +} + +// SetForkHeight mocks base method. +func (m *MockState) SetForkHeight(arg0 uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetForkHeight", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetForkHeight indicates an expected call of SetForkHeight. +func (mr *MockStateMockRecorder) SetForkHeight(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetForkHeight", reflect.TypeOf((*MockState)(nil).SetForkHeight), arg0) +} + +// SetIndexHasReset mocks base method. +func (m *MockState) SetIndexHasReset() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetIndexHasReset") + ret0, _ := ret[0].(error) + return ret0 +} + +// SetIndexHasReset indicates an expected call of SetIndexHasReset. +func (mr *MockStateMockRecorder) SetIndexHasReset() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIndexHasReset", reflect.TypeOf((*MockState)(nil).SetIndexHasReset)) +} + +// SetLastAccepted mocks base method. +func (m *MockState) SetLastAccepted(arg0 ids.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetLastAccepted", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetLastAccepted indicates an expected call of SetLastAccepted. +func (mr *MockStateMockRecorder) SetLastAccepted(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastAccepted", reflect.TypeOf((*MockState)(nil).SetLastAccepted), arg0) +} diff --git a/avalanchego/vms/proposervm/state/state.go b/avalanchego/vms/proposervm/state/state.go index 53341356..95d301f1 100644 --- a/avalanchego/vms/proposervm/state/state.go +++ b/avalanchego/vms/proposervm/state/state.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/proposervm/state/state_test.go b/avalanchego/vms/proposervm/state/state_test.go index a4cff327..7ee8815d 100644 --- a/avalanchego/vms/proposervm/state/state_test.go +++ b/avalanchego/vms/proposervm/state/state_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package state diff --git a/avalanchego/vms/proposervm/state_summary.go b/avalanchego/vms/proposervm/state_summary.go index f8fb30aa..5e50dad5 100644 --- a/avalanchego/vms/proposervm/state_summary.go +++ b/avalanchego/vms/proposervm/state_summary.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm diff --git a/avalanchego/vms/proposervm/state_syncable_vm.go b/avalanchego/vms/proposervm/state_syncable_vm.go index cc15dcf4..e28cb798 100644 --- a/avalanchego/vms/proposervm/state_syncable_vm.go +++ b/avalanchego/vms/proposervm/state_syncable_vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm diff --git a/avalanchego/vms/proposervm/state_syncable_vm_test.go b/avalanchego/vms/proposervm/state_syncable_vm_test.go index 7114e0ba..1ad05bc2 100644 --- a/avalanchego/vms/proposervm/state_syncable_vm_test.go +++ b/avalanchego/vms/proposervm/state_syncable_vm_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -6,7 +6,7 @@ package proposervm import ( "bytes" "crypto" - "fmt" + "errors" "testing" "time" @@ -86,7 +86,7 @@ func helperBuildStateSyncTestObjects(t *testing.T) (*fullVM, *VM) { innerVM.GetBlockF = func(i ids.ID) (snowman.Block, error) { return innerGenesisBlk, nil } // createVM - vm := New(innerVM, time.Time{}, uint64(0)) + vm := New(innerVM, time.Time{}, uint64(0), time.Time{}) ctx := snow.DefaultContextTest() ctx.NodeID = ids.NodeIDFromCert(pTestCert.Leaf) @@ -176,7 +176,7 @@ func TestStateSyncGetOngoingSyncStateSummary(t *testing.T) { return innerBlk, nil } - slb, err := statelessblock.Build( + slb, err := statelessblock.BuildApricot( vm.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -258,7 +258,7 @@ func TestStateSyncGetLastStateSummary(t *testing.T) { return innerBlk, nil } - slb, err := statelessblock.Build( + slb, err := statelessblock.BuildApricot( vm.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -343,7 +343,7 @@ func TestStateSyncGetStateSummary(t *testing.T) { return innerBlk, nil } - slb, err := statelessblock.Build( + slb, err := statelessblock.BuildApricot( vm.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -413,7 +413,7 @@ func TestParseStateSummary(t *testing.T) { return innerBlk, nil } - slb, err := statelessblock.Build( + slb, err := statelessblock.BuildApricot( vm.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -473,7 +473,7 @@ func TestStateSummaryAccept(t *testing.T) { return innerBlk, nil } - slb, err := statelessblock.Build( + slb, err := statelessblock.BuildApricot( vm.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -543,7 +543,7 @@ func TestStateSummaryAcceptOlderBlock(t *testing.T) { return innerBlk, nil } - slb, err := statelessblock.Build( + slb, err := statelessblock.BuildApricot( vm.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -593,7 +593,7 @@ func TestNoStateSummariesServedWhileRepairingHeightIndex(t *testing.T) { } coreVM.GetStateSummaryF = func(height uint64) (block.StateSummary, error) { if height != summaryHeight { - return nil, fmt.Errorf("requested unexpected summary") + return nil, errors.New("requested unexpected summary") } return coreStateSummary, nil } diff --git a/avalanchego/vms/proposervm/summary/build.go b/avalanchego/vms/proposervm/summary/build.go index 9dc18786..484dd8cb 100644 --- a/avalanchego/vms/proposervm/summary/build.go +++ b/avalanchego/vms/proposervm/summary/build.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package summary diff --git a/avalanchego/vms/proposervm/summary/build_test.go b/avalanchego/vms/proposervm/summary/build_test.go index 550caf7e..361ec364 100644 --- a/avalanchego/vms/proposervm/summary/build_test.go +++ b/avalanchego/vms/proposervm/summary/build_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package summary diff --git a/avalanchego/vms/proposervm/summary/codec.go b/avalanchego/vms/proposervm/summary/codec.go index 8f2647ff..2ad2508e 100644 --- a/avalanchego/vms/proposervm/summary/codec.go +++ b/avalanchego/vms/proposervm/summary/codec.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package summary diff --git a/avalanchego/vms/proposervm/summary/parse.go b/avalanchego/vms/proposervm/summary/parse.go index fc718507..34538ac6 100644 --- a/avalanchego/vms/proposervm/summary/parse.go +++ b/avalanchego/vms/proposervm/summary/parse.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package summary diff --git a/avalanchego/vms/proposervm/summary/parse_test.go b/avalanchego/vms/proposervm/summary/parse_test.go index 72da941b..14f586cf 100644 --- a/avalanchego/vms/proposervm/summary/parse_test.go +++ b/avalanchego/vms/proposervm/summary/parse_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package summary diff --git a/avalanchego/vms/proposervm/summary/state_summary.go b/avalanchego/vms/proposervm/summary/state_summary.go index 71d52156..badbddea 100644 --- a/avalanchego/vms/proposervm/summary/state_summary.go +++ b/avalanchego/vms/proposervm/summary/state_summary.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package summary diff --git a/avalanchego/vms/proposervm/tree/tree.go b/avalanchego/vms/proposervm/tree/tree.go index c9d3354b..92ed031a 100644 --- a/avalanchego/vms/proposervm/tree/tree.go +++ b/avalanchego/vms/proposervm/tree/tree.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package tree diff --git a/avalanchego/vms/proposervm/tree/tree_test.go b/avalanchego/vms/proposervm/tree/tree_test.go index 92cb0410..40f25127 100644 --- a/avalanchego/vms/proposervm/tree/tree_test.go +++ b/avalanchego/vms/proposervm/tree/tree_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package tree diff --git a/avalanchego/vms/proposervm/vm.go b/avalanchego/vms/proposervm/vm.go index 8eccfd5e..ffa6acde 100644 --- a/avalanchego/vms/proposervm/vm.go +++ b/avalanchego/vms/proposervm/vm.go @@ -1,15 +1,21 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm import ( "context" + "errors" "fmt" "time" + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" + "github.com/ava-labs/avalanchego/api/metrics" + "github.com/ava-labs/avalanchego/cache" + "github.com/ava-labs/avalanchego/cache/metercacher" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/manager" "github.com/ava-labs/avalanchego/database/prefixdb" @@ -37,6 +43,7 @@ const ( // are only specific to the second. minBlockDelay = time.Second checkIndexedFrequency = 10 * time.Second + innerBlkCacheSize = 512 ) var ( @@ -46,6 +53,8 @@ var ( _ block.StateSyncableVM = &VM{} dbPrefix = []byte("proposervm") + + errBanffBlockBeforeBanff = errors.New("block requiring Banff issued before Banff activated") ) type VM struct { @@ -57,6 +66,8 @@ type VM struct { activationTime time.Time minimumPChainHeight uint64 + banffActivationTime time.Time + state.State hIndexer indexer.HeightIndexer resetHeightIndexOngoing utils.AtomicBool @@ -74,6 +85,11 @@ type VM struct { // Each element is a block that passed verification but // hasn't yet been accepted/rejected verifiedBlocks map[ids.ID]PostForkBlock + // Stateless block ID --> inner block. + // Only contains post-fork blocks near the tip so that the cache doesn't get + // filled with random blocks every time this node parses blocks while + // processing a GetAncestors message from a bootstrapping node. + innerBlkCache cache.Cacher preferred ids.ID consensusState snow.State context context.Context @@ -86,12 +102,15 @@ type VM struct { // lastAcceptedHeight is set to the last accepted PostForkBlock's height. lastAcceptedHeight uint64 + + activationTimeBanff time.Time } func New( vm block.ChainVM, activationTime time.Time, minimumPChainHeight uint64, + banffActivationTime time.Time, ) *VM { bVM, _ := vm.(block.BatchedChainVM) hVM, _ := vm.(block.HeightIndexedChainVM) @@ -104,6 +123,8 @@ func New( activationTime: activationTime, minimumPChainHeight: minimumPChainHeight, + + banffActivationTime: banffActivationTime, } } @@ -117,6 +138,23 @@ func (vm *VM) Initialize( fxs []*common.Fx, appSender common.AppSender, ) error { + // TODO: Add a helper for this metrics override, it is performed in multiple + // places. + multiGatherer := metrics.NewMultiGatherer() + registerer := prometheus.NewRegistry() + if err := multiGatherer.Register("proposervm", registerer); err != nil { + return err + } + + optionalGatherer := metrics.NewOptionalGatherer() + if err := multiGatherer.Register("", optionalGatherer); err != nil { + return err + } + if err := ctx.Metrics.Register(multiGatherer); err != nil { + return err + } + ctx.Metrics = optionalGatherer + vm.ctx = ctx rawDB := dbManager.Current().Database prefixDB := prefixdb.New(dbPrefix, rawDB) @@ -124,8 +162,18 @@ func (vm *VM) Initialize( vm.State = state.New(vm.db) vm.Windower = proposer.New(ctx.ValidatorState, ctx.SubnetID, ctx.ChainID) vm.Tree = tree.New() + innerBlkCache, err := metercacher.New( + "inner_block_cache", + registerer, + &cache.LRU{Size: innerBlkCacheSize}, + ) + if err != nil { + return err + } + vm.innerBlkCache = innerBlkCache indexerDB := versiondb.New(vm.db) + // TODO: Use [state.NewMetered] here to populate additional metrics. indexerState := state.New(indexerDB) vm.hIndexer = indexer.NewHeightIndexer(vm, vm.ctx.Log, indexerState) @@ -142,7 +190,7 @@ func (vm *VM) Initialize( vm.context = context vm.onShutdown = cancel - err := vm.ChainVM.Initialize( + err = vm.ChainVM.Initialize( ctx, dbManager, genesisBytes, @@ -570,11 +618,18 @@ func (vm *VM) setLastAcceptedMetadata() error { } func (vm *VM) parsePostForkBlock(b []byte) (PostForkBlock, error) { - statelessBlock, err := statelessblock.Parse(b) + statelessBlock, requireBanff, err := statelessblock.Parse(b) if err != nil { return nil, err } + if requireBanff { + banffActivated := vm.Clock.Time().After(vm.activationTimeBanff) + if !banffActivated { + return nil, errBanffBlockBeforeBanff + } + } + // if the block already exists, then make sure the status is set correctly blkID := statelessBlock.ID() blk, err := vm.getPostForkBlock(blkID) @@ -586,7 +641,7 @@ func (vm *VM) parsePostForkBlock(b []byte) (PostForkBlock, error) { } innerBlkBytes := statelessBlock.Block() - innerBlk, err := vm.ChainVM.ParseBlock(innerBlkBytes) + innerBlk, err := vm.parseInnerBlock(blkID, innerBlkBytes) if err != nil { return nil, err } @@ -640,7 +695,7 @@ func (vm *VM) getPostForkBlock(blkID ids.ID) (PostForkBlock, error) { } innerBlkBytes := statelessBlock.Block() - innerBlk, err := vm.ChainVM.ParseBlock(innerBlkBytes) + innerBlk, err := vm.parseInnerBlock(blkID, innerBlkBytes) if err != nil { return nil, err } @@ -723,3 +778,28 @@ func (vm *VM) optimalPChainHeight(minPChainHeight uint64) (uint64, error) { return math.Max64(minimumHeight, minPChainHeight), nil } + +// parseInnerBlock attempts to parse the provided bytes as an inner block. If +// the inner block happens to be cached, then the inner block will not be +// parsed. +func (vm *VM) parseInnerBlock(outerBlkID ids.ID, innerBlkBytes []byte) (snowman.Block, error) { + if innerBlkIntf, ok := vm.innerBlkCache.Get(outerBlkID); ok { + return innerBlkIntf.(snowman.Block), nil + } + + innerBlk, err := vm.ChainVM.ParseBlock(innerBlkBytes) + if err != nil { + return nil, err + } + vm.cacheInnerBlock(outerBlkID, innerBlk) + return innerBlk, nil +} + +// Caches proposervm block ID --> inner block if the inner block's height +// is within [innerBlkCacheSize] of the last accepted block's height. +func (vm *VM) cacheInnerBlock(outerBlkID ids.ID, innerBlk snowman.Block) { + diff := math.Diff64(innerBlk.Height(), vm.lastAcceptedHeight) + if diff < innerBlkCacheSize { + vm.innerBlkCache.Put(outerBlkID, innerBlk) + } +} diff --git a/avalanchego/vms/proposervm/vm_byzantine_test.go b/avalanchego/vms/proposervm/vm_byzantine_test.go index 49d646e7..0dc346d8 100644 --- a/avalanchego/vms/proposervm/vm_byzantine_test.go +++ b/avalanchego/vms/proposervm/vm_byzantine_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -91,6 +91,7 @@ func TestInvalidByzantineProposerParent(t *testing.T) { // Ensure that a byzantine node issuing an invalid PreForkBlock (Y or Z) when // the parent block (X) is issued into a PostForkBlock (A) will be marked as // invalid correctly. +// // G // / | // A - X @@ -209,6 +210,7 @@ func TestInvalidByzantineProposerOracleParent(t *testing.T) { // Ensure that a byzantine node issuing an invalid PostForkBlock (B) when the // parent block (X) is issued into a PostForkBlock (A) will be marked as invalid // correctly. +// // G // / | // A - X @@ -274,7 +276,7 @@ func TestInvalidByzantineProposerPreForkParent(t *testing.T) { } } - bStatelessBlock, err := block.BuildUnsigned( + bStatelessBlock, err := block.BuildUnsignedApricot( xBlock.ID(), yBlock.Timestamp(), 0, @@ -312,6 +314,7 @@ func TestInvalidByzantineProposerPreForkParent(t *testing.T) { // Ensure that a byzantine node issuing an invalid OptionBlock (B) which // contains core block (Y) whose parent (G) doesn't match (B)'s parent (A)'s // inner block (X) will be marked as invalid correctly. +// // G // / | \ // A - X | @@ -475,7 +478,7 @@ func TestBlockVerify_InvalidPostForkOption(t *testing.T) { TimestampV: coreGenBlk.Timestamp(), } - ySlb, err := block.BuildUnsigned( + ySlb, err := block.BuildUnsignedApricot( coreGenBlk.ID(), coreGenBlk.Timestamp(), uint64(2000), @@ -733,6 +736,7 @@ func TestGetBlock_MutatedSignature(t *testing.T) { // GetBlock shouldn't really be able to succeed, as we don't have a valid // representation of [blkID] + proVM.innerBlkCache.Flush() // So we don't get from the cache fetchedBlk, err := proVM.GetBlock(blkID) if err != nil { t.Skip(err) diff --git a/avalanchego/vms/proposervm/vm_test.go b/avalanchego/vms/proposervm/vm_test.go index bf185588..dd3eb1e3 100644 --- a/avalanchego/vms/proposervm/vm_test.go +++ b/avalanchego/vms/proposervm/vm_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package proposervm @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/database/manager" @@ -20,11 +22,13 @@ import ( "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/avalanchego/snow/engine/snowman/block/mocks" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/staking" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/proposervm/proposer" + "github.com/ava-labs/avalanchego/vms/proposervm/state" statelessblock "github.com/ava-labs/avalanchego/vms/proposervm/block" ) @@ -122,7 +126,7 @@ func initTestProposerVM( } } - proVM := New(coreVM, proBlkStartTime, minPChainHeight) + proVM := New(coreVM, proBlkStartTime, minPChainHeight, time.Time{}) valState := &validators.TestState{ T: t, @@ -472,7 +476,7 @@ func TestCoreBlockFailureCauseProposerBlockParseFailure(t *testing.T) { coreVM.ParseBlockF = func(b []byte) (snowman.Block, error) { return nil, errMarshallingFailed } - slb, err := statelessblock.Build( + slb, err := statelessblock.BuildApricot( proVM.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -517,7 +521,7 @@ func TestTwoProBlocksWrappingSameCoreBlockCanBeParsed(t *testing.T) { return innerBlk, nil } - slb1, err := statelessblock.Build( + slb1, err := statelessblock.BuildApricot( proVM.preferred, innerBlk.Timestamp(), 100, // pChainHeight, @@ -538,7 +542,7 @@ func TestTwoProBlocksWrappingSameCoreBlockCanBeParsed(t *testing.T) { }, } - slb2, err := statelessblock.Build( + slb2, err := statelessblock.BuildApricot( proVM.preferred, innerBlk.Timestamp(), 200, // pChainHeight, @@ -630,7 +634,7 @@ func TestTwoProBlocksWithSameParentCanBothVerify(t *testing.T) { t.Fatal("could not retrieve pChain height") } - netSlb, err := statelessblock.BuildUnsigned( + netSlb, err := statelessblock.BuildUnsignedApricot( proVM.preferred, netcoreBlk.Timestamp(), pChainHeight, @@ -861,7 +865,7 @@ func TestExpiredBuildBlock(t *testing.T) { } } - proVM := New(coreVM, time.Time{}, 0) + proVM := New(coreVM, time.Time{}, 0, time.Time{}) valState := &validators.TestState{ T: t, @@ -931,7 +935,7 @@ func TestExpiredBuildBlock(t *testing.T) { HeightV: coreGenBlk.Height() + 1, TimestampV: coreGenBlk.Timestamp(), } - statelessBlock, err := statelessblock.BuildUnsigned( + statelessBlock, err := statelessblock.BuildUnsignedApricot( coreGenBlk.ID(), coreBlk.Timestamp(), 0, @@ -1036,7 +1040,7 @@ func TestInnerBlockDeduplication(t *testing.T) { coreBlk1 := &wrappedBlock{ Block: coreBlk, } - statelessBlock0, err := statelessblock.BuildUnsigned( + statelessBlock0, err := statelessblock.BuildUnsignedApricot( coreGenBlk.ID(), coreBlk.Timestamp(), 0, @@ -1045,7 +1049,7 @@ func TestInnerBlockDeduplication(t *testing.T) { if err != nil { t.Fatal(err) } - statelessBlock1, err := statelessblock.BuildUnsigned( + statelessBlock1, err := statelessblock.BuildUnsignedApricot( coreGenBlk.ID(), coreBlk.Timestamp(), 1, @@ -1191,7 +1195,7 @@ func TestInnerVMRollback(t *testing.T) { dbManager := manager.NewMemDB(version.Semantic1_0_0) - proVM := New(coreVM, time.Time{}, 0) + proVM := New(coreVM, time.Time{}, 0, time.Time{}) if err := proVM.Initialize(ctx, dbManager, nil, nil, nil, nil, nil, nil); err != nil { t.Fatalf("failed to initialize proposerVM with %s", err) @@ -1215,7 +1219,7 @@ func TestInnerVMRollback(t *testing.T) { HeightV: coreGenBlk.Height() + 1, TimestampV: coreGenBlk.Timestamp(), } - statelessBlock, err := statelessblock.BuildUnsigned( + statelessBlock, err := statelessblock.BuildUnsignedApricot( coreGenBlk.ID(), coreBlk.Timestamp(), 0, @@ -1282,7 +1286,7 @@ func TestInnerVMRollback(t *testing.T) { coreBlk.StatusV = choices.Processing - proVM = New(coreVM, time.Time{}, 0) + proVM = New(coreVM, time.Time{}, 0, time.Time{}) if err := proVM.Initialize(ctx, dbManager, nil, nil, nil, nil, nil, nil); err != nil { t.Fatalf("failed to initialize proposerVM with %s", err) @@ -1336,7 +1340,7 @@ func TestBuildBlockDuringWindow(t *testing.T) { HeightV: coreBlk0.Height() + 1, TimestampV: coreBlk0.Timestamp(), } - statelessBlock0, err := statelessblock.BuildUnsigned( + statelessBlock0, err := statelessblock.BuildUnsignedApricot( coreGenBlk.ID(), coreBlk0.Timestamp(), 0, @@ -1414,6 +1418,7 @@ func TestBuildBlockDuringWindow(t *testing.T) { // Ensure that Accepting a PostForkBlock (A) containing core block (X) causes // core block (Y) and (Z) to also be rejected. +// // G // / \ // A(X) B(Y) @@ -1457,7 +1462,7 @@ func TestTwoForks_OneIsAccepted(t *testing.T) { TimestampV: gBlock.Timestamp(), } - ySlb, err := statelessblock.BuildUnsigned( + ySlb, err := statelessblock.BuildUnsignedApricot( gBlock.ID(), gBlock.Timestamp(), defaultPChainHeight, @@ -1568,7 +1573,7 @@ func TestTooFarAdvanced(t *testing.T) { t.Fatalf("could not verify valid block due to %s", err) } - ySlb, err := statelessblock.BuildUnsigned( + ySlb, err := statelessblock.BuildUnsignedApricot( aBlock.ID(), aBlock.Timestamp().Add(maxSkew), defaultPChainHeight, @@ -1591,7 +1596,7 @@ func TestTooFarAdvanced(t *testing.T) { t.Fatal("should have errored errProposerWindowNotStarted") } - ySlb, err = statelessblock.BuildUnsigned( + ySlb, err = statelessblock.BuildUnsignedApricot( aBlock.ID(), aBlock.Timestamp().Add(proposer.MaxDelay), defaultPChainHeight, @@ -1618,6 +1623,7 @@ func TestTooFarAdvanced(t *testing.T) { // Ensure that Accepting a PostForkOption (B) causes both the other option and // the core block in the other option to be rejected. +// // G // | // A(X) @@ -1806,7 +1812,7 @@ func TestRejectedHeightNotIndexed(t *testing.T) { } } - proVM := New(coreVM, time.Time{}, 0) + proVM := New(coreVM, time.Time{}, 0, time.Time{}) valState := &validators.TestState{ T: t, @@ -1883,7 +1889,7 @@ func TestRejectedHeightNotIndexed(t *testing.T) { TimestampV: coreGenBlk.Timestamp(), } - ySlb, err := statelessblock.BuildUnsigned( + ySlb, err := statelessblock.BuildUnsignedApricot( coreGenBlk.ID(), coreGenBlk.Timestamp(), defaultPChainHeight, @@ -1984,7 +1990,7 @@ func TestRejectedOptionHeightNotIndexed(t *testing.T) { } } - proVM := New(coreVM, time.Time{}, 0) + proVM := New(coreVM, time.Time{}, 0, time.Time{}) valState := &validators.TestState{ T: t, @@ -2109,3 +2115,96 @@ func TestRejectedOptionHeightNotIndexed(t *testing.T) { require.NoError(err) require.Equal(bBlock.ID(), blkID) } + +func TestVMInnerBlkCache(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create a VM + innerVM := mocks.NewMockChainVM(ctrl) + vm := New( + innerVM, + time.Time{}, // fork is active + 0, // minimum P-Chain height + time.Time{}, // fork is active + ) + + dummyDBManager := manager.NewMemDB(version.Semantic1_0_0) + // make sure that DBs are compressed correctly + dummyDBManager = dummyDBManager.NewPrefixDBManager([]byte{}) + + innerVM.EXPECT().Initialize( + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(nil) + + ctx := snow.DefaultContextTest() + ctx.NodeID = ids.NodeIDFromCert(pTestCert.Leaf) + ctx.StakingCertLeaf = pTestCert.Leaf + ctx.StakingLeafSigner = pTestCert.PrivateKey.(crypto.Signer) + + err := vm.Initialize( + ctx, + dummyDBManager, + nil, + nil, + nil, + nil, + nil, + nil, + ) + require.NoError(err) + state := state.NewMockState(ctrl) // mock state + vm.State = state + + // Create a block near the tip (0). + blkNearTipInnerBytes := []byte{1} + blkNearTip, err := statelessblock.BuildBanff( + ids.GenerateTestID(), // parent + time.Time{}, // timestamp + 1, // pChainHeight, + vm.ctx.StakingCertLeaf, // cert + blkNearTipInnerBytes, // inner blk bytes + vm.ctx.ChainID, // chain ID + vm.ctx.StakingLeafSigner, // key + ) + require.NoError(err) + + // Parse a block. + // Not in the VM's state so need to parse it. + state.EXPECT().GetBlock(blkNearTip.ID()).Return(blkNearTip, choices.Accepted, nil).Times(2) + // We will ask the inner VM to parse. + mockInnerBlkNearTip := snowman.NewMockBlock(ctrl) + mockInnerBlkNearTip.EXPECT().Height().Return(uint64(1)).Times(2) + innerVM.EXPECT().ParseBlock(blkNearTipInnerBytes).Return(mockInnerBlkNearTip, nil).Times(2) + _, err = vm.ParseBlock(blkNearTip.Bytes()) + require.NoError(err) + + // Block should now be in cache because it's a post-fork block + // and close to the tip. + gotBlk, ok := vm.innerBlkCache.Get(blkNearTip.ID()) + require.True(ok) + require.Equal(mockInnerBlkNearTip, gotBlk) + require.Equal(uint64(0), vm.lastAcceptedHeight) + + // Clear the cache + vm.innerBlkCache.Flush() + + // Advance the tip height + vm.lastAcceptedHeight = innerBlkCacheSize + 1 + + // Parse the block again. This time it shouldn't be cached + // because it's not close to the tip. + _, err = vm.ParseBlock(blkNearTip.Bytes()) + require.NoError(err) + + _, ok = vm.innerBlkCache.Get(blkNearTip.ID()) + require.False(ok) +} diff --git a/avalanchego/vms/registry/mock_vm_getter.go b/avalanchego/vms/registry/mock_vm_getter.go index b1d97a6b..f8affd2d 100644 --- a/avalanchego/vms/registry/mock_vm_getter.go +++ b/avalanchego/vms/registry/mock_vm_getter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // Code generated by MockGen. DO NOT EDIT. diff --git a/avalanchego/vms/registry/mock_vm_registerer.go b/avalanchego/vms/registry/mock_vm_registerer.go index dd00f124..2d88894e 100644 --- a/avalanchego/vms/registry/mock_vm_registerer.go +++ b/avalanchego/vms/registry/mock_vm_registerer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // Code generated by MockGen. DO NOT EDIT. diff --git a/avalanchego/vms/registry/mock_vm_registry.go b/avalanchego/vms/registry/mock_vm_registry.go index d46647bf..50b1009a 100644 --- a/avalanchego/vms/registry/mock_vm_registry.go +++ b/avalanchego/vms/registry/mock_vm_registry.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // Code generated by MockGen. DO NOT EDIT. diff --git a/avalanchego/vms/registry/vm_getter.go b/avalanchego/vms/registry/vm_getter.go index 0b338985..18601135 100644 --- a/avalanchego/vms/registry/vm_getter.go +++ b/avalanchego/vms/registry/vm_getter.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package registry diff --git a/avalanchego/vms/registry/vm_getter_test.go b/avalanchego/vms/registry/vm_getter_test.go index 13d99834..4322aa33 100644 --- a/avalanchego/vms/registry/vm_getter_test.go +++ b/avalanchego/vms/registry/vm_getter_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package registry diff --git a/avalanchego/vms/registry/vm_registerer.go b/avalanchego/vms/registry/vm_registerer.go index bb489d30..40d373e3 100644 --- a/avalanchego/vms/registry/vm_registerer.go +++ b/avalanchego/vms/registry/vm_registerer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package registry diff --git a/avalanchego/vms/registry/vm_registerer_test.go b/avalanchego/vms/registry/vm_registerer_test.go index be37ab5b..1812a60a 100644 --- a/avalanchego/vms/registry/vm_registerer_test.go +++ b/avalanchego/vms/registry/vm_registerer_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package registry diff --git a/avalanchego/vms/registry/vm_registry.go b/avalanchego/vms/registry/vm_registry.go index 7ddf9c38..ff54a362 100644 --- a/avalanchego/vms/registry/vm_registry.go +++ b/avalanchego/vms/registry/vm_registry.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package registry diff --git a/avalanchego/vms/registry/vm_registry_test.go b/avalanchego/vms/registry/vm_registry_test.go index 18aca9e2..ffbe3ef9 100644 --- a/avalanchego/vms/registry/vm_registry_test.go +++ b/avalanchego/vms/registry/vm_registry_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package registry diff --git a/avalanchego/vms/rpcchainvm/errors.go b/avalanchego/vms/rpcchainvm/errors.go index d977b819..aee26427 100644 --- a/avalanchego/vms/rpcchainvm/errors.go +++ b/avalanchego/vms/rpcchainvm/errors.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm diff --git a/avalanchego/vms/rpcchainvm/factory.go b/avalanchego/vms/rpcchainvm/factory.go index 80c53d3c..f55945b0 100644 --- a/avalanchego/vms/rpcchainvm/factory.go +++ b/avalanchego/vms/rpcchainvm/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm diff --git a/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_client.go b/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_client.go index b1dbd18a..cc91e682 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_client.go +++ b/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gconn diff --git a/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_server.go b/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_server.go index 46ac22b4..6576e5c4 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_server.go +++ b/avalanchego/vms/rpcchainvm/ghttp/gconn/conn_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gconn diff --git a/avalanchego/vms/rpcchainvm/ghttp/greader/reader_client.go b/avalanchego/vms/rpcchainvm/ghttp/greader/reader_client.go index cf7eade3..ce0702d5 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/greader/reader_client.go +++ b/avalanchego/vms/rpcchainvm/ghttp/greader/reader_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package greader diff --git a/avalanchego/vms/rpcchainvm/ghttp/greader/reader_server.go b/avalanchego/vms/rpcchainvm/ghttp/greader/reader_server.go index 44e90072..0fd5a2b5 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/greader/reader_server.go +++ b/avalanchego/vms/rpcchainvm/ghttp/greader/reader_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package greader diff --git a/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/locked_writer.go b/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/locked_writer.go index 328b788f..5a3290e8 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/locked_writer.go +++ b/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/locked_writer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gresponsewriter diff --git a/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go b/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go index c5f7549b..f34a393a 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go +++ b/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gresponsewriter diff --git a/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go b/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go index 869d5f15..38c1ad63 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go +++ b/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gresponsewriter diff --git a/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_client.go b/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_client.go index 23c71433..5fdc224b 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_client.go +++ b/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gwriter diff --git a/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_server.go b/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_server.go index 503db67d..52f57ebe 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_server.go +++ b/avalanchego/vms/rpcchainvm/ghttp/gwriter/writer_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gwriter diff --git a/avalanchego/vms/rpcchainvm/ghttp/http_client.go b/avalanchego/vms/rpcchainvm/ghttp/http_client.go index 1ec1f7bf..5119dcf2 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/http_client.go +++ b/avalanchego/vms/rpcchainvm/ghttp/http_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ghttp diff --git a/avalanchego/vms/rpcchainvm/ghttp/http_server.go b/avalanchego/vms/rpcchainvm/ghttp/http_server.go index bb0ba2a7..440e433b 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/http_server.go +++ b/avalanchego/vms/rpcchainvm/ghttp/http_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ghttp diff --git a/avalanchego/vms/rpcchainvm/ghttp/http_test.go b/avalanchego/vms/rpcchainvm/ghttp/http_test.go index 901c2660..73e51e6c 100644 --- a/avalanchego/vms/rpcchainvm/ghttp/http_test.go +++ b/avalanchego/vms/rpcchainvm/ghttp/http_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package ghttp diff --git a/avalanchego/vms/rpcchainvm/grpcutils/listener.go b/avalanchego/vms/rpcchainvm/grpcutils/listener.go index 276d4b2b..23663f39 100644 --- a/avalanchego/vms/rpcchainvm/grpcutils/listener.go +++ b/avalanchego/vms/rpcchainvm/grpcutils/listener.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package grpcutils diff --git a/avalanchego/vms/rpcchainvm/grpcutils/server_closer.go b/avalanchego/vms/rpcchainvm/grpcutils/server_closer.go index a51323eb..a7b0b453 100644 --- a/avalanchego/vms/rpcchainvm/grpcutils/server_closer.go +++ b/avalanchego/vms/rpcchainvm/grpcutils/server_closer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package grpcutils diff --git a/avalanchego/vms/rpcchainvm/grpcutils/util.go b/avalanchego/vms/rpcchainvm/grpcutils/util.go index 21aab7b9..9cd2f183 100644 --- a/avalanchego/vms/rpcchainvm/grpcutils/util.go +++ b/avalanchego/vms/rpcchainvm/grpcutils/util.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package grpcutils diff --git a/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_client.go b/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_client.go index 4ef8e4f9..ddb77cb2 100644 --- a/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_client.go +++ b/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gsubnetlookup diff --git a/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_server.go b/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_server.go index 4205b29c..78cfde3f 100644 --- a/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_server.go +++ b/avalanchego/vms/rpcchainvm/gsubnetlookup/subnet_lookup_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package gsubnetlookup diff --git a/avalanchego/vms/rpcchainvm/messenger/messenger_client.go b/avalanchego/vms/rpcchainvm/messenger/messenger_client.go index f4cbec84..718fa739 100644 --- a/avalanchego/vms/rpcchainvm/messenger/messenger_client.go +++ b/avalanchego/vms/rpcchainvm/messenger/messenger_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package messenger diff --git a/avalanchego/vms/rpcchainvm/messenger/messenger_server.go b/avalanchego/vms/rpcchainvm/messenger/messenger_server.go index f4455d45..2e6ed5df 100644 --- a/avalanchego/vms/rpcchainvm/messenger/messenger_server.go +++ b/avalanchego/vms/rpcchainvm/messenger/messenger_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package messenger diff --git a/avalanchego/vms/rpcchainvm/plugin_test.go b/avalanchego/vms/rpcchainvm/plugin_test.go index 7b28b9bb..87645d07 100644 --- a/avalanchego/vms/rpcchainvm/plugin_test.go +++ b/avalanchego/vms/rpcchainvm/plugin_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm diff --git a/avalanchego/vms/rpcchainvm/state_syncable_vm_test.go b/avalanchego/vms/rpcchainvm/state_syncable_vm_test.go index 6a8ea2e6..8d1a3232 100644 --- a/avalanchego/vms/rpcchainvm/state_syncable_vm_test.go +++ b/avalanchego/vms/rpcchainvm/state_syncable_vm_test.go @@ -1,10 +1,10 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm import ( - "fmt" + "errors" "testing" "github.com/golang/mock/gomock" @@ -57,8 +57,8 @@ var ( } // a fictitious error unrelated to state sync - errBrokenConnectionOrSomething = fmt.Errorf("brokenConnectionOrSomething") - errNothingToParse = fmt.Errorf("nil summary bytes. Nothing to parse") + errBrokenConnectionOrSomething = errors.New("brokenConnectionOrSomething") + errNothingToParse = errors.New("nil summary bytes. Nothing to parse") ) type StateSyncEnabledMock struct { diff --git a/avalanchego/vms/rpcchainvm/vm.go b/avalanchego/vms/rpcchainvm/vm.go index db44db36..b71d956e 100644 --- a/avalanchego/vms/rpcchainvm/vm.go +++ b/avalanchego/vms/rpcchainvm/vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm @@ -18,7 +18,7 @@ import ( // protocolVersion should be bumped anytime changes are made which require // the plugin vm to upgrade to latest avalanchego release to be compatible. -const protocolVersion = 15 +const protocolVersion = 17 var ( // Handshake is a common handshake that is shared by plugin and host. diff --git a/avalanchego/vms/rpcchainvm/vm_client.go b/avalanchego/vms/rpcchainvm/vm_client.go index b5deefbc..369ae59e 100644 --- a/avalanchego/vms/rpcchainvm/vm_client.go +++ b/avalanchego/vms/rpcchainvm/vm_client.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm diff --git a/avalanchego/vms/rpcchainvm/vm_server.go b/avalanchego/vms/rpcchainvm/vm_server.go index 81ab5617..aab9195f 100644 --- a/avalanchego/vms/rpcchainvm/vm_server.go +++ b/avalanchego/vms/rpcchainvm/vm_server.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm diff --git a/avalanchego/vms/rpcchainvm/vm_test.go b/avalanchego/vms/rpcchainvm/vm_test.go index 3c82af6d..f92decdb 100644 --- a/avalanchego/vms/rpcchainvm/vm_test.go +++ b/avalanchego/vms/rpcchainvm/vm_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package rpcchainvm @@ -146,7 +146,7 @@ func Test_VMCreateHandlers(t *testing.T) { require.NoErrorf(err, "failed to create listener: %v", err) go func() { - err := http.Serve(listener, r) + err := http.Serve(listener, r) // #nosec G114 require.NoErrorf(err, "failed to serve HTTP: %v", err) }() diff --git a/avalanchego/vms/secp256k1fx/credential.go b/avalanchego/vms/secp256k1fx/credential.go index 8add2f4c..6d2e4803 100644 --- a/avalanchego/vms/secp256k1fx/credential.go +++ b/avalanchego/vms/secp256k1fx/credential.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/credential_test.go b/avalanchego/vms/secp256k1fx/credential_test.go index 5521215e..6a6e660b 100644 --- a/avalanchego/vms/secp256k1fx/credential_test.go +++ b/avalanchego/vms/secp256k1fx/credential_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/factory.go b/avalanchego/vms/secp256k1fx/factory.go index c918a4d8..9952fb66 100644 --- a/avalanchego/vms/secp256k1fx/factory.go +++ b/avalanchego/vms/secp256k1fx/factory.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/factory_test.go b/avalanchego/vms/secp256k1fx/factory_test.go index 4dab87a2..644fe21f 100644 --- a/avalanchego/vms/secp256k1fx/factory_test.go +++ b/avalanchego/vms/secp256k1fx/factory_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/fx.go b/avalanchego/vms/secp256k1fx/fx.go index 08dc41b7..4c4bb242 100644 --- a/avalanchego/vms/secp256k1fx/fx.go +++ b/avalanchego/vms/secp256k1fx/fx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/fx_test.go b/avalanchego/vms/secp256k1fx/fx_test.go index dd38c55a..930863a0 100644 --- a/avalanchego/vms/secp256k1fx/fx_test.go +++ b/avalanchego/vms/secp256k1fx/fx_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/input.go b/avalanchego/vms/secp256k1fx/input.go index d229554b..4c1fda2c 100644 --- a/avalanchego/vms/secp256k1fx/input.go +++ b/avalanchego/vms/secp256k1fx/input.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/input_test.go b/avalanchego/vms/secp256k1fx/input_test.go index d1adf34a..d9f44ee2 100644 --- a/avalanchego/vms/secp256k1fx/input_test.go +++ b/avalanchego/vms/secp256k1fx/input_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/keychain.go b/avalanchego/vms/secp256k1fx/keychain.go index 64c9258d..92496cf4 100644 --- a/avalanchego/vms/secp256k1fx/keychain.go +++ b/avalanchego/vms/secp256k1fx/keychain.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/keychain_test.go b/avalanchego/vms/secp256k1fx/keychain_test.go index ee6b6589..604d4bfd 100644 --- a/avalanchego/vms/secp256k1fx/keychain_test.go +++ b/avalanchego/vms/secp256k1fx/keychain_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/mint_operation.go b/avalanchego/vms/secp256k1fx/mint_operation.go index 748de3a3..7b04380d 100644 --- a/avalanchego/vms/secp256k1fx/mint_operation.go +++ b/avalanchego/vms/secp256k1fx/mint_operation.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/mint_operation_test.go b/avalanchego/vms/secp256k1fx/mint_operation_test.go index a25832a5..7793a480 100644 --- a/avalanchego/vms/secp256k1fx/mint_operation_test.go +++ b/avalanchego/vms/secp256k1fx/mint_operation_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/mint_output.go b/avalanchego/vms/secp256k1fx/mint_output.go index c3544d95..7f293f21 100644 --- a/avalanchego/vms/secp256k1fx/mint_output.go +++ b/avalanchego/vms/secp256k1fx/mint_output.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/mint_output_test.go b/avalanchego/vms/secp256k1fx/mint_output_test.go index f2c7e931..98f917e7 100644 --- a/avalanchego/vms/secp256k1fx/mint_output_test.go +++ b/avalanchego/vms/secp256k1fx/mint_output_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/output_owners.go b/avalanchego/vms/secp256k1fx/output_owners.go index 628634c7..a2962135 100644 --- a/avalanchego/vms/secp256k1fx/output_owners.go +++ b/avalanchego/vms/secp256k1fx/output_owners.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/output_owners_test.go b/avalanchego/vms/secp256k1fx/output_owners_test.go index 0bb02afe..ac268d21 100644 --- a/avalanchego/vms/secp256k1fx/output_owners_test.go +++ b/avalanchego/vms/secp256k1fx/output_owners_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/transfer_input.go b/avalanchego/vms/secp256k1fx/transfer_input.go index 5071c47c..58e33913 100644 --- a/avalanchego/vms/secp256k1fx/transfer_input.go +++ b/avalanchego/vms/secp256k1fx/transfer_input.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/transfer_input_test.go b/avalanchego/vms/secp256k1fx/transfer_input_test.go index 3b4a20ee..2c923ebd 100644 --- a/avalanchego/vms/secp256k1fx/transfer_input_test.go +++ b/avalanchego/vms/secp256k1fx/transfer_input_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/transfer_output.go b/avalanchego/vms/secp256k1fx/transfer_output.go index b4d2dad1..6327d774 100644 --- a/avalanchego/vms/secp256k1fx/transfer_output.go +++ b/avalanchego/vms/secp256k1fx/transfer_output.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/transfer_output_test.go b/avalanchego/vms/secp256k1fx/transfer_output_test.go index 08692d7f..21a117ac 100644 --- a/avalanchego/vms/secp256k1fx/transfer_output_test.go +++ b/avalanchego/vms/secp256k1fx/transfer_output_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/tx.go b/avalanchego/vms/secp256k1fx/tx.go index c2a12971..fe7179b5 100644 --- a/avalanchego/vms/secp256k1fx/tx.go +++ b/avalanchego/vms/secp256k1fx/tx.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/secp256k1fx/vm.go b/avalanchego/vms/secp256k1fx/vm.go index 3d160a88..d692fea5 100644 --- a/avalanchego/vms/secp256k1fx/vm.go +++ b/avalanchego/vms/secp256k1fx/vm.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package secp256k1fx diff --git a/avalanchego/vms/types/blob_data.go b/avalanchego/vms/types/blob_data.go index 765dc59b..1e6f9ed4 100644 --- a/avalanchego/vms/types/blob_data.go +++ b/avalanchego/vms/types/blob_data.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package types diff --git a/avalanchego/wallet/chain/p/backend.go b/avalanchego/wallet/chain/p/backend.go index cff29bbe..8f267a6d 100644 --- a/avalanchego/wallet/chain/p/backend.go +++ b/avalanchego/wallet/chain/p/backend.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p diff --git a/avalanchego/wallet/chain/p/backend_visitor.go b/avalanchego/wallet/chain/p/backend_visitor.go index 7717fb3c..02ae9ae1 100644 --- a/avalanchego/wallet/chain/p/backend_visitor.go +++ b/avalanchego/wallet/chain/p/backend_visitor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p @@ -44,6 +44,10 @@ func (b *backendVisitor) CreateSubnetTx(tx *txs.CreateSubnetTx) error { return b.baseTx(&tx.BaseTx) } +func (b *backendVisitor) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { + return b.baseTx(&tx.BaseTx) +} + func (b *backendVisitor) ImportTx(tx *txs.ImportTx) error { err := b.b.removeUTXOs( b.ctx, @@ -77,6 +81,18 @@ func (b *backendVisitor) ExportTx(tx *txs.ExportTx) error { return b.baseTx(&tx.BaseTx) } +func (b *backendVisitor) TransformSubnetTx(tx *txs.TransformSubnetTx) error { + return b.baseTx(&tx.BaseTx) +} + +func (b *backendVisitor) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { + return b.baseTx(&tx.BaseTx) +} + +func (b *backendVisitor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { + return b.baseTx(&tx.BaseTx) +} + func (b *backendVisitor) baseTx(tx *txs.BaseTx) error { return b.b.removeUTXOs( b.ctx, diff --git a/avalanchego/wallet/chain/p/builder.go b/avalanchego/wallet/chain/p/builder.go index 43c8e727..6615d41f 100644 --- a/avalanchego/wallet/chain/p/builder.go +++ b/avalanchego/wallet/chain/p/builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p @@ -6,6 +6,7 @@ package p import ( "errors" "fmt" + "time" stdcontext "context" @@ -13,6 +14,7 @@ import ( "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/platformvm/stakeable" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/validator" @@ -84,6 +86,14 @@ type Builder interface { options ...common.Option, ) (*txs.AddSubnetValidatorTx, error) + // NewRemoveSubnetValidatorTx removes [nodeID] from the validator + // set [subnetID]. + NewRemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + options ...common.Option, + ) (*txs.RemoveSubnetValidatorTx, error) + // NewAddDelegatorTx creates a new delegator to a validator on the primary // network. // @@ -144,6 +154,94 @@ type Builder interface { outputs []*avax.TransferableOutput, options ...common.Option, ) (*txs.ExportTx, error) + + // NewTransformSubnetTx creates a transform subnet transaction that attempts + // to convert the provided [subnetID] from a permissioned subnet to a + // permissionless subnet. This transaction will convert + // [maxSupply] - [initialSupply] of [assetID] to staking rewards. + // + // - [subnetID] specifies the subnet to transform. + // - [assetID] specifies the asset to use to reward stakers on the subnet. + // - [initialSupply] is the amount of [assetID] that will be in circulation + // after this transaction is accepted. + // - [maxSupply] is the maximum total amount of [assetID] that should ever + // exist. + // - [minConsumptionRate] is the rate that a staker will receive rewards + // if they stake with a duration of 0. + // - [maxConsumptionRate] is the maximum rate that staking rewards should be + // consumed from the reward pool per year. + // - [minValidatorStake] is the minimum amount of funds required to become a + // validator. + // - [maxValidatorStake] is the maximum amount of funds a single validator + // can be allocated, including delegated funds. + // - [minStakeDuration] is the minimum number of seconds a staker can stake + // for. + // - [maxStakeDuration] is the maximum number of seconds a staker can stake + // for. + // - [minValidatorStake] is the minimum amount of funds required to become a + // delegator. + // - [maxValidatorWeightFactor] is the factor which calculates the maximum + // amount of delegation a validator can receive. A value of 1 effectively + // disables delegation. + // - [uptimeRequirement] is the minimum percentage a validator must be + // online and responsive to receive a reward. + NewTransformSubnetTx( + subnetID ids.ID, + assetID ids.ID, + initialSupply uint64, + maxSupply uint64, + minConsumptionRate uint64, + maxConsumptionRate uint64, + minValidatorStake uint64, + maxValidatorStake uint64, + minStakeDuration time.Duration, + maxStakeDuration time.Duration, + minDelegationFee uint32, + minDelegatorStake uint64, + maxValidatorWeightFactor byte, + uptimeRequirement uint32, + options ...common.Option, + ) (*txs.TransformSubnetTx, error) + + // NewAddPermissionlessValidatorTx creates a new validator of the specified + // subnet. + // + // - [vdr] specifies all the details of the validation period such as the + // subnetID, startTime, endTime, stake weight, and nodeID. + // - [signer] if the subnetID is the primary network, this is the BLS key + // for this validator. Otherwise, this value should be the empty signer. + // - [assetID] specifies the asset to stake. + // - [validationRewardsOwner] specifies the owner of all the rewards this + // validator earns for its validation period. + // - [delegationRewardsOwner] specifies the owner of all the rewards this + // validator earns for delegations during its validation period. + // - [shares] specifies the fraction (out of 1,000,000) that this validator + // will take from delegation rewards. If 1,000,000 is provided, 100% of + // the delegation reward will be sent to the validator's [rewardsOwner]. + NewAddPermissionlessValidatorTx( + vdr *validator.SubnetValidator, + signer signer.Signer, + assetID ids.ID, + validationRewardsOwner *secp256k1fx.OutputOwners, + delegationRewardsOwner *secp256k1fx.OutputOwners, + shares uint32, + options ...common.Option, + ) (*txs.AddPermissionlessValidatorTx, error) + + // NewAddPermissionlessDelegatorTx creates a new delegator of the specified + // subnet on the specified nodeID. + // + // - [vdr] specifies all the details of the delegation period such as the + // subnetID, startTime, endTime, stake weight, and nodeID. + // - [assetID] specifies the asset to stake. + // - [rewardsOwner] specifies the owner of all the rewards this delegator + // earns during its delegation period. + NewAddPermissionlessDelegatorTx( + vdr *validator.SubnetValidator, + assetID ids.ID, + rewardsOwner *secp256k1fx.OutputOwners, + options ...common.Option, + ) (*txs.AddPermissionlessDelegatorTx, error) } // BuilderBackend specifies the required information needed to build unsigned @@ -161,10 +259,10 @@ type builder struct { // NewBuilder returns a new transaction builder. // -// - [addrs] is the set of addresses that the builder assumes can be used when -// signing the transactions in the future. -// - [backend] provides the required access to the chain's context and state to -// build out the transactions. +// - [addrs] is the set of addresses that the builder assumes can be used when +// signing the transactions in the future. +// - [backend] provides the required access to the chain's context and state +// to build out the transactions. func NewBuilder(addrs ids.ShortSet, backend BuilderBackend) Builder { return &builder{ addrs: addrs, @@ -230,9 +328,12 @@ func (b *builder) NewAddValidatorTx( shares uint32, options ...common.Option, ) (*txs.AddValidatorTx, error) { - toBurn := map[ids.ID]uint64{} + avaxAssetID := b.backend.AVAXAssetID() + toBurn := map[ids.ID]uint64{ + avaxAssetID: b.backend.AddPrimaryNetworkValidatorFee(), + } toStake := map[ids.ID]uint64{ - b.backend.AVAXAssetID(): vdr.Wght, + avaxAssetID: vdr.Wght, } ops := common.NewOptions(options) inputs, baseOutputs, stakeOutputs, err := b.spend(toBurn, toStake, ops) @@ -249,10 +350,10 @@ func (b *builder) NewAddValidatorTx( Outs: baseOutputs, Memo: ops.Memo(), }}, - Validator: *vdr, - Stake: stakeOutputs, - RewardsOwner: rewardsOwner, - Shares: shares, + Validator: *vdr, + StakeOuts: stakeOutputs, + RewardsOwner: rewardsOwner, + DelegationShares: shares, }, nil } @@ -261,7 +362,7 @@ func (b *builder) NewAddSubnetValidatorTx( options ...common.Option, ) (*txs.AddSubnetValidatorTx, error) { toBurn := map[ids.ID]uint64{ - b.backend.AVAXAssetID(): b.backend.CreateSubnetTxFee(), + b.backend.AVAXAssetID(): b.backend.AddSubnetValidatorFee(), } toStake := map[ids.ID]uint64{} ops := common.NewOptions(options) @@ -288,12 +389,49 @@ func (b *builder) NewAddSubnetValidatorTx( }, nil } +func (b *builder) NewRemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + options ...common.Option, +) (*txs.RemoveSubnetValidatorTx, error) { + toBurn := map[ids.ID]uint64{ + b.backend.AVAXAssetID(): b.backend.BaseTxFee(), + } + toStake := map[ids.ID]uint64{} + ops := common.NewOptions(options) + inputs, outputs, _, err := b.spend(toBurn, toStake, ops) + if err != nil { + return nil, err + } + + subnetAuth, err := b.authorizeSubnet(subnetID, ops) + if err != nil { + return nil, err + } + + return &txs.RemoveSubnetValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: b.backend.NetworkID(), + BlockchainID: constants.PlatformChainID, + Ins: inputs, + Outs: outputs, + Memo: ops.Memo(), + }}, + Subnet: subnetID, + NodeID: nodeID, + SubnetAuth: subnetAuth, + }, nil +} + func (b *builder) NewAddDelegatorTx( vdr *validator.Validator, rewardsOwner *secp256k1fx.OutputOwners, options ...common.Option, ) (*txs.AddDelegatorTx, error) { - toBurn := map[ids.ID]uint64{} + avaxAssetID := b.backend.AVAXAssetID() + toBurn := map[ids.ID]uint64{ + avaxAssetID: b.backend.AddPrimaryNetworkDelegatorFee(), + } toStake := map[ids.ID]uint64{ b.backend.AVAXAssetID(): vdr.Wght, } @@ -312,9 +450,9 @@ func (b *builder) NewAddDelegatorTx( Outs: baseOutputs, Memo: ops.Memo(), }}, - Validator: *vdr, - Stake: stakeOutputs, - RewardsOwner: rewardsOwner, + Validator: *vdr, + StakeOuts: stakeOutputs, + DelegationRewardsOwner: rewardsOwner, }, nil } @@ -327,7 +465,7 @@ func (b *builder) NewCreateChainTx( options ...common.Option, ) (*txs.CreateChainTx, error) { toBurn := map[ids.ID]uint64{ - b.backend.AVAXAssetID(): b.backend.CreateSubnetTxFee(), + b.backend.AVAXAssetID(): b.backend.CreateBlockchainTxFee(), } toStake := map[ids.ID]uint64{} ops := common.NewOptions(options) @@ -530,6 +668,148 @@ func (b *builder) NewExportTx( }, nil } +func (b *builder) NewTransformSubnetTx( + subnetID ids.ID, + assetID ids.ID, + initialSupply uint64, + maxSupply uint64, + minConsumptionRate uint64, + maxConsumptionRate uint64, + minValidatorStake uint64, + maxValidatorStake uint64, + minStakeDuration time.Duration, + maxStakeDuration time.Duration, + minDelegationFee uint32, + minDelegatorStake uint64, + maxValidatorWeightFactor byte, + uptimeRequirement uint32, + options ...common.Option, +) (*txs.TransformSubnetTx, error) { + toBurn := map[ids.ID]uint64{ + b.backend.AVAXAssetID(): b.backend.TransformSubnetTxFee(), + assetID: maxSupply - initialSupply, + } + toStake := map[ids.ID]uint64{} + ops := common.NewOptions(options) + inputs, outputs, _, err := b.spend(toBurn, toStake, ops) + if err != nil { + return nil, err + } + + subnetAuth, err := b.authorizeSubnet(subnetID, ops) + if err != nil { + return nil, err + } + + return &txs.TransformSubnetTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: b.backend.NetworkID(), + BlockchainID: constants.PlatformChainID, + Ins: inputs, + Outs: outputs, + Memo: ops.Memo(), + }}, + Subnet: subnetID, + AssetID: assetID, + InitialSupply: initialSupply, + MaximumSupply: maxSupply, + MinConsumptionRate: minConsumptionRate, + MaxConsumptionRate: maxConsumptionRate, + MinValidatorStake: minValidatorStake, + MaxValidatorStake: maxValidatorStake, + MinStakeDuration: uint32(minStakeDuration / time.Second), + MaxStakeDuration: uint32(maxStakeDuration / time.Second), + MinDelegationFee: minDelegationFee, + MinDelegatorStake: minDelegatorStake, + MaxValidatorWeightFactor: maxValidatorWeightFactor, + UptimeRequirement: uptimeRequirement, + SubnetAuth: subnetAuth, + }, nil +} + +func (b *builder) NewAddPermissionlessValidatorTx( + vdr *validator.SubnetValidator, + signer signer.Signer, + assetID ids.ID, + validationRewardsOwner *secp256k1fx.OutputOwners, + delegationRewardsOwner *secp256k1fx.OutputOwners, + shares uint32, + options ...common.Option, +) (*txs.AddPermissionlessValidatorTx, error) { + avaxAssetID := b.backend.AVAXAssetID() + toBurn := map[ids.ID]uint64{} + if vdr.Subnet == constants.PrimaryNetworkID { + toBurn[avaxAssetID] = b.backend.AddPrimaryNetworkValidatorFee() + } else { + toBurn[avaxAssetID] = b.backend.AddSubnetValidatorFee() + } + toStake := map[ids.ID]uint64{ + assetID: vdr.Wght, + } + ops := common.NewOptions(options) + inputs, baseOutputs, stakeOutputs, err := b.spend(toBurn, toStake, ops) + if err != nil { + return nil, err + } + + ids.SortShortIDs(validationRewardsOwner.Addrs) + ids.SortShortIDs(delegationRewardsOwner.Addrs) + return &txs.AddPermissionlessValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: b.backend.NetworkID(), + BlockchainID: constants.PlatformChainID, + Ins: inputs, + Outs: baseOutputs, + Memo: ops.Memo(), + }}, + Validator: vdr.Validator, + Subnet: vdr.Subnet, + Signer: signer, + StakeOuts: stakeOutputs, + ValidatorRewardsOwner: validationRewardsOwner, + DelegatorRewardsOwner: delegationRewardsOwner, + DelegationShares: shares, + }, nil +} + +func (b *builder) NewAddPermissionlessDelegatorTx( + vdr *validator.SubnetValidator, + assetID ids.ID, + rewardsOwner *secp256k1fx.OutputOwners, + options ...common.Option, +) (*txs.AddPermissionlessDelegatorTx, error) { + avaxAssetID := b.backend.AVAXAssetID() + toBurn := map[ids.ID]uint64{} + if vdr.Subnet == constants.PrimaryNetworkID { + toBurn[avaxAssetID] = b.backend.AddPrimaryNetworkDelegatorFee() + } else { + toBurn[avaxAssetID] = b.backend.AddSubnetDelegatorFee() + } + toStake := map[ids.ID]uint64{ + assetID: vdr.Wght, + } + ops := common.NewOptions(options) + inputs, baseOutputs, stakeOutputs, err := b.spend(toBurn, toStake, ops) + if err != nil { + return nil, err + } + + ids.SortShortIDs(rewardsOwner.Addrs) + return &txs.AddPermissionlessDelegatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: b.backend.NetworkID(), + BlockchainID: constants.PlatformChainID, + Ins: inputs, + Outs: baseOutputs, + Memo: ops.Memo(), + }}, + Validator: vdr.Validator, + Subnet: vdr.Subnet, + StakeOuts: stakeOutputs, + DelegationRewardsOwner: rewardsOwner, + }, nil +} + func (b *builder) getBalance( chainID ids.ID, options *common.Options, @@ -580,15 +860,15 @@ func (b *builder) getBalance( // spend takes in the requested burn amounts and the requested stake amounts. // -// - [amountsToBurn] maps assetID to the amount of the asset to spend without -// producing an output. This is typically used for fees. However, it can also -// be used to consume some of an asset that will be produced in separate -// outputs, such as ExportedOutputs. Only unlocked UTXOs are able to be -// burned here. -// - [amountsToStake] maps assetID to the amount of the asset to spend and place -// into the staked outputs. First locked UTXOs are attempted to be used for -// these funds, and then unlocked UTXOs will be attempted to be used. There is -// no preferential ordering on the unlock times. +// - [amountsToBurn] maps assetID to the amount of the asset to spend without +// producing an output. This is typically used for fees. However, it can +// also be used to consume some of an asset that will be produced in +// separate outputs, such as ExportedOutputs. Only unlocked UTXOs are able +// to be burned here. +// - [amountsToStake] maps assetID to the amount of the asset to spend and +// place into the staked outputs. First locked UTXOs are attempted to be +// used for these funds, and then unlocked UTXOs will be attempted to be +// used. There is no preferential ordering on the unlock times. func (b *builder) spend( amountsToBurn map[ids.ID]uint64, amountsToStake map[ids.ID]uint64, diff --git a/avalanchego/wallet/chain/p/builder_with_options.go b/avalanchego/wallet/chain/p/builder_with_options.go index b37d48f6..bd597e39 100644 --- a/avalanchego/wallet/chain/p/builder_with_options.go +++ b/avalanchego/wallet/chain/p/builder_with_options.go @@ -1,11 +1,14 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p import ( + "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/validator" "github.com/ava-labs/avalanchego/vms/secp256k1fx" @@ -22,10 +25,10 @@ type builderWithOptions struct { // NewBuilderWithOptions returns a new transaction builder that will use the // given options by default. // -// - [builder] is the builder that will be called to perform the underlying -// opterations. -// - [options] will be provided to the builder in addition to the options -// provided in the method calls. +// - [builder] is the builder that will be called to perform the underlying +// opterations. +// - [options] will be provided to the builder in addition to the options +// provided in the method calls. func NewBuilderWithOptions(builder Builder, options ...common.Option) Builder { return &builderWithOptions{ Builder: builder, @@ -75,6 +78,18 @@ func (b *builderWithOptions) NewAddSubnetValidatorTx( ) } +func (b *builderWithOptions) RemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + options ...common.Option, +) (*txs.RemoveSubnetValidatorTx, error) { + return b.Builder.NewRemoveSubnetValidatorTx( + nodeID, + subnetID, + common.UnionOptions(b.options, options)..., + ) +} + func (b *builderWithOptions) NewAddDelegatorTx( vdr *validator.Validator, rewardsOwner *secp256k1fx.OutputOwners, @@ -138,3 +153,73 @@ func (b *builderWithOptions) NewExportTx( common.UnionOptions(b.options, options)..., ) } + +func (b *builderWithOptions) NewTransformSubnetTx( + subnetID ids.ID, + assetID ids.ID, + initialSupply uint64, + maxSupply uint64, + minConsumptionRate uint64, + maxConsumptionRate uint64, + minValidatorStake uint64, + maxValidatorStake uint64, + minStakeDuration time.Duration, + maxStakeDuration time.Duration, + minDelegationFee uint32, + minDelegatorStake uint64, + maxValidatorWeightFactor byte, + uptimeRequirement uint32, + options ...common.Option, +) (*txs.TransformSubnetTx, error) { + return b.Builder.NewTransformSubnetTx( + subnetID, + assetID, + initialSupply, + maxSupply, + minConsumptionRate, + maxConsumptionRate, + minValidatorStake, + maxValidatorStake, + minStakeDuration, + maxStakeDuration, + minDelegationFee, + minDelegatorStake, + maxValidatorWeightFactor, + uptimeRequirement, + common.UnionOptions(b.options, options)..., + ) +} + +func (b *builderWithOptions) NewAddPermissionlessValidatorTx( + vdr *validator.SubnetValidator, + signer signer.Signer, + assetID ids.ID, + validationRewardsOwner *secp256k1fx.OutputOwners, + delegationRewardsOwner *secp256k1fx.OutputOwners, + shares uint32, + options ...common.Option, +) (*txs.AddPermissionlessValidatorTx, error) { + return b.Builder.NewAddPermissionlessValidatorTx( + vdr, + signer, + assetID, + validationRewardsOwner, + delegationRewardsOwner, + shares, + common.UnionOptions(b.options, options)..., + ) +} + +func (b *builderWithOptions) NewAddPermissionlessDelegatorTx( + vdr *validator.SubnetValidator, + assetID ids.ID, + rewardsOwner *secp256k1fx.OutputOwners, + options ...common.Option, +) (*txs.AddPermissionlessDelegatorTx, error) { + return b.Builder.NewAddPermissionlessDelegatorTx( + vdr, + assetID, + rewardsOwner, + common.UnionOptions(b.options, options)..., + ) +} diff --git a/avalanchego/wallet/chain/p/context.go b/avalanchego/wallet/chain/p/context.go index f8bf35ae..715991a4 100644 --- a/avalanchego/wallet/chain/p/context.go +++ b/avalanchego/wallet/chain/p/context.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p @@ -18,15 +18,25 @@ type Context interface { AVAXAssetID() ids.ID BaseTxFee() uint64 CreateSubnetTxFee() uint64 + TransformSubnetTxFee() uint64 CreateBlockchainTxFee() uint64 + AddPrimaryNetworkValidatorFee() uint64 + AddPrimaryNetworkDelegatorFee() uint64 + AddSubnetValidatorFee() uint64 + AddSubnetDelegatorFee() uint64 } type context struct { - networkID uint32 - avaxAssetID ids.ID - baseTxFee uint64 - createSubnetTxFee uint64 - createBlockchainTxFee uint64 + networkID uint32 + avaxAssetID ids.ID + baseTxFee uint64 + createSubnetTxFee uint64 + transformSubnetTxFee uint64 + createBlockchainTxFee uint64 + addPrimaryNetworkValidatorFee uint64 + addPrimaryNetworkDelegatorFee uint64 + addSubnetValidatorFee uint64 + addSubnetDelegatorFee uint64 } func NewContextFromURI(ctx stdcontext.Context, uri string) (Context, error) { @@ -60,7 +70,12 @@ func NewContextFromClients( asset.AssetID, uint64(txFees.TxFee), uint64(txFees.CreateSubnetTxFee), + uint64(txFees.TransformSubnetTxFee), uint64(txFees.CreateBlockchainTxFee), + uint64(txFees.AddPrimaryNetworkValidatorFee), + uint64(txFees.AddPrimaryNetworkDelegatorFee), + uint64(txFees.AddSubnetValidatorFee), + uint64(txFees.AddSubnetDelegatorFee), ), nil } @@ -69,19 +84,34 @@ func NewContext( avaxAssetID ids.ID, baseTxFee uint64, createSubnetTxFee uint64, + transformSubnetTxFee uint64, createBlockchainTxFee uint64, + addPrimaryNetworkValidatorFee uint64, + addPrimaryNetworkDelegatorFee uint64, + addSubnetValidatorFee uint64, + addSubnetDelegatorFee uint64, ) Context { return &context{ - networkID: networkID, - avaxAssetID: avaxAssetID, - baseTxFee: baseTxFee, - createSubnetTxFee: createSubnetTxFee, - createBlockchainTxFee: createBlockchainTxFee, + networkID: networkID, + avaxAssetID: avaxAssetID, + baseTxFee: baseTxFee, + createSubnetTxFee: createSubnetTxFee, + transformSubnetTxFee: transformSubnetTxFee, + createBlockchainTxFee: createBlockchainTxFee, + addPrimaryNetworkValidatorFee: addPrimaryNetworkValidatorFee, + addPrimaryNetworkDelegatorFee: addPrimaryNetworkDelegatorFee, + addSubnetValidatorFee: addSubnetValidatorFee, + addSubnetDelegatorFee: addSubnetDelegatorFee, } } -func (c *context) NetworkID() uint32 { return c.networkID } -func (c *context) AVAXAssetID() ids.ID { return c.avaxAssetID } -func (c *context) BaseTxFee() uint64 { return c.baseTxFee } -func (c *context) CreateSubnetTxFee() uint64 { return c.createSubnetTxFee } -func (c *context) CreateBlockchainTxFee() uint64 { return c.createBlockchainTxFee } +func (c *context) NetworkID() uint32 { return c.networkID } +func (c *context) AVAXAssetID() ids.ID { return c.avaxAssetID } +func (c *context) BaseTxFee() uint64 { return c.baseTxFee } +func (c *context) CreateSubnetTxFee() uint64 { return c.createSubnetTxFee } +func (c *context) TransformSubnetTxFee() uint64 { return c.transformSubnetTxFee } +func (c *context) CreateBlockchainTxFee() uint64 { return c.createBlockchainTxFee } +func (c *context) AddPrimaryNetworkValidatorFee() uint64 { return c.addPrimaryNetworkValidatorFee } +func (c *context) AddPrimaryNetworkDelegatorFee() uint64 { return c.addPrimaryNetworkDelegatorFee } +func (c *context) AddSubnetValidatorFee() uint64 { return c.addSubnetValidatorFee } +func (c *context) AddSubnetDelegatorFee() uint64 { return c.addSubnetDelegatorFee } diff --git a/avalanchego/wallet/chain/p/signer.go b/avalanchego/wallet/chain/p/signer.go index a507a93f..69fc5fff 100644 --- a/avalanchego/wallet/chain/p/signer.go +++ b/avalanchego/wallet/chain/p/signer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p @@ -12,7 +12,7 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) -var _ Signer = &signer{} +var _ Signer = &txSigner{} type Signer interface { SignUnsigned(ctx stdcontext.Context, tx txs.UnsignedTx) (*txs.Tx, error) @@ -24,24 +24,24 @@ type SignerBackend interface { GetTx(ctx stdcontext.Context, txID ids.ID) (*txs.Tx, error) } -type signer struct { +type txSigner struct { kc *secp256k1fx.Keychain backend SignerBackend } func NewSigner(kc *secp256k1fx.Keychain, backend SignerBackend) Signer { - return &signer{ + return &txSigner{ kc: kc, backend: backend, } } -func (s *signer) SignUnsigned(ctx stdcontext.Context, utx txs.UnsignedTx) (*txs.Tx, error) { +func (s *txSigner) SignUnsigned(ctx stdcontext.Context, utx txs.UnsignedTx) (*txs.Tx, error) { tx := &txs.Tx{Unsigned: utx} return tx, s.Sign(ctx, tx) } -func (s *signer) Sign(ctx stdcontext.Context, tx *txs.Tx) error { +func (s *txSigner) Sign(ctx stdcontext.Context, tx *txs.Tx) error { return tx.Unsigned.Visit(&signerVisitor{ kc: s.kc, backend: s.backend, diff --git a/avalanchego/wallet/chain/p/signer_visitor.go b/avalanchego/wallet/chain/p/signer_visitor.go index 51cb2824..f72683c3 100644 --- a/avalanchego/wallet/chain/p/signer_visitor.go +++ b/avalanchego/wallet/chain/p/signer_visitor.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p @@ -116,10 +116,57 @@ func (s *signerVisitor) ExportTx(tx *txs.ExportTx) error { return s.sign(s.tx, txSigners) } +func (s *signerVisitor) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { + txSigners, err := s.getSigners(constants.PlatformChainID, tx.Ins) + if err != nil { + return err + } + subnetAuthSigners, err := s.getSubnetSigners(tx.Subnet, tx.SubnetAuth) + if err != nil { + return err + } + txSigners = append(txSigners, subnetAuthSigners) + return s.sign(s.tx, txSigners) +} + +func (s *signerVisitor) TransformSubnetTx(tx *txs.TransformSubnetTx) error { + txSigners, err := s.getSigners(constants.PlatformChainID, tx.Ins) + if err != nil { + return err + } + subnetAuthSigners, err := s.getSubnetSigners(tx.Subnet, tx.SubnetAuth) + if err != nil { + return err + } + txSigners = append(txSigners, subnetAuthSigners) + return s.sign(s.tx, txSigners) +} + +func (s *signerVisitor) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { + txSigners, err := s.getSigners(constants.PlatformChainID, tx.Ins) + if err != nil { + return err + } + return s.sign(s.tx, txSigners) +} + +func (s *signerVisitor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { + txSigners, err := s.getSigners(constants.PlatformChainID, tx.Ins) + if err != nil { + return err + } + return s.sign(s.tx, txSigners) +} + func (s *signerVisitor) getSigners(sourceChainID ids.ID, ins []*avax.TransferableInput) ([][]*crypto.PrivateKeySECP256K1R, error) { txSigners := make([][]*crypto.PrivateKeySECP256K1R, len(ins)) for credIndex, transferInput := range ins { - input, ok := transferInput.In.(*secp256k1fx.TransferInput) + inIntf := transferInput.In + if stakeableIn, ok := inIntf.(*stakeable.LockIn); ok { + inIntf = stakeableIn.TransferableIn + } + + input, ok := inIntf.(*secp256k1fx.TransferInput) if !ok { return nil, errUnknownInputType } diff --git a/avalanchego/wallet/chain/p/wallet.go b/avalanchego/wallet/chain/p/wallet.go index ba748a24..b5ecb9a1 100644 --- a/avalanchego/wallet/chain/p/wallet.go +++ b/avalanchego/wallet/chain/p/wallet.go @@ -1,14 +1,16 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p import ( "errors" + "time" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/validator" @@ -69,6 +71,16 @@ type Wallet interface { options ...common.Option, ) (ids.ID, error) + // IssueAddSubnetValidatorTx creates, signs, and issues a transaction that + // removes a validator of a subnet. + // + // - [nodeID] is the validator being removed from [subnetID]. + IssueRemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + options ...common.Option, + ) (ids.ID, error) + // IssueAddDelegatorTx creates, signs, and issues a new delegator to a // validator on the primary network. // @@ -132,6 +144,94 @@ type Wallet interface { options ...common.Option, ) (ids.ID, error) + // IssueTransformSubnetTx creates a transform subnet transaction that attempts + // to convert the provided [subnetID] from a permissioned subnet to a + // permissionless subnet. This transaction will convert + // [maxSupply] - [initialSupply] of [assetID] to staking rewards. + // + // - [subnetID] specifies the subnet to transform. + // - [assetID] specifies the asset to use to reward stakers on the subnet. + // - [initialSupply] is the amount of [assetID] that will be in circulation + // after this transaction is accepted. + // - [maxSupply] is the maximum total amount of [assetID] that should ever + // exist. + // - [minConsumptionRate] is the rate that a staker will receive rewards + // if they stake with a duration of 0. + // - [maxConsumptionRate] is the maximum rate that staking rewards should be + // consumed from the reward pool per year. + // - [minValidatorStake] is the minimum amount of funds required to become a + // validator. + // - [maxValidatorStake] is the maximum amount of funds a single validator + // can be allocated, including delegated funds. + // - [minStakeDuration] is the minimum number of seconds a staker can stake + // for. + // - [maxStakeDuration] is the maximum number of seconds a staker can stake + // for. + // - [minValidatorStake] is the minimum amount of funds required to become a + // delegator. + // - [maxValidatorWeightFactor] is the factor which calculates the maximum + // amount of delegation a validator can receive. A value of 1 effectively + // disables delegation. + // - [uptimeRequirement] is the minimum percentage a validator must be + // online and responsive to receive a reward. + IssueTransformSubnetTx( + subnetID ids.ID, + assetID ids.ID, + initialSupply uint64, + maxSupply uint64, + minConsumptionRate uint64, + maxConsumptionRate uint64, + minValidatorStake uint64, + maxValidatorStake uint64, + minStakeDuration time.Duration, + maxStakeDuration time.Duration, + minDelegationFee uint32, + minDelegatorStake uint64, + maxValidatorWeightFactor byte, + uptimeRequirement uint32, + options ...common.Option, + ) (ids.ID, error) + + // IssueAddPermissionlessValidatorTx creates, signs, and issues a new + // validator of the specified subnet. + // + // - [vdr] specifies all the details of the validation period such as the + // subnetID, startTime, endTime, stake weight, and nodeID. + // - [signer] if the subnetID is the primary network, this is the BLS key + // for this validator. Otherwise, this value should be the empty signer. + // - [assetID] specifies the asset to stake. + // - [validationRewardsOwner] specifies the owner of all the rewards this + // validator earns for its validation period. + // - [delegationRewardsOwner] specifies the owner of all the rewards this + // validator earns for delegations during its validation period. + // - [shares] specifies the fraction (out of 1,000,000) that this validator + // will take from delegation rewards. If 1,000,000 is provided, 100% of + // the delegation reward will be sent to the validator's [rewardsOwner]. + IssueAddPermissionlessValidatorTx( + vdr *validator.SubnetValidator, + signer signer.Signer, + assetID ids.ID, + validationRewardsOwner *secp256k1fx.OutputOwners, + delegationRewardsOwner *secp256k1fx.OutputOwners, + shares uint32, + options ...common.Option, + ) (ids.ID, error) + + // IssueAddPermissionlessDelegatorTx creates, signs, and issues a new + // delegator of the specified subnet on the specified nodeID. + // + // - [vdr] specifies all the details of the delegation period such as the + // subnetID, startTime, endTime, stake weight, and nodeID. + // - [assetID] specifies the asset to stake. + // - [rewardsOwner] specifies the owner of all the rewards this delegator + // earns during its delegation period. + IssueAddPermissionlessDelegatorTx( + vdr *validator.SubnetValidator, + assetID ids.ID, + rewardsOwner *secp256k1fx.OutputOwners, + options ...common.Option, + ) (ids.ID, error) + // IssueUnsignedTx signs and issues the unsigned tx. IssueUnsignedTx( utx txs.UnsignedTx, @@ -205,6 +305,18 @@ func (w *wallet) IssueAddSubnetValidatorTx( return w.IssueUnsignedTx(utx, options...) } +func (w *wallet) IssueRemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + options ...common.Option, +) (ids.ID, error) { + utx, err := w.builder.NewRemoveSubnetValidatorTx(nodeID, subnetID, options...) + if err != nil { + return ids.Empty, err + } + return w.IssueUnsignedTx(utx, options...) +} + func (w *wallet) IssueAddDelegatorTx( vdr *validator.Validator, rewardsOwner *secp256k1fx.OutputOwners, @@ -267,6 +379,88 @@ func (w *wallet) IssueExportTx( return w.IssueUnsignedTx(utx, options...) } +func (w *wallet) IssueTransformSubnetTx( + subnetID ids.ID, + assetID ids.ID, + initialSupply uint64, + maxSupply uint64, + minConsumptionRate uint64, + maxConsumptionRate uint64, + minValidatorStake uint64, + maxValidatorStake uint64, + minStakeDuration time.Duration, + maxStakeDuration time.Duration, + minDelegationFee uint32, + minDelegatorStake uint64, + maxValidatorWeightFactor byte, + uptimeRequirement uint32, + options ...common.Option, +) (ids.ID, error) { + utx, err := w.builder.NewTransformSubnetTx( + subnetID, + assetID, + initialSupply, + maxSupply, + minConsumptionRate, + maxConsumptionRate, + minValidatorStake, + maxValidatorStake, + minStakeDuration, + maxStakeDuration, + minDelegationFee, + minDelegatorStake, + maxValidatorWeightFactor, + uptimeRequirement, + options..., + ) + if err != nil { + return ids.Empty, err + } + return w.IssueUnsignedTx(utx, options...) +} + +func (w *wallet) IssueAddPermissionlessValidatorTx( + vdr *validator.SubnetValidator, + signer signer.Signer, + assetID ids.ID, + validationRewardsOwner *secp256k1fx.OutputOwners, + delegationRewardsOwner *secp256k1fx.OutputOwners, + shares uint32, + options ...common.Option, +) (ids.ID, error) { + utx, err := w.builder.NewAddPermissionlessValidatorTx( + vdr, + signer, + assetID, + validationRewardsOwner, + delegationRewardsOwner, + shares, + options..., + ) + if err != nil { + return ids.Empty, err + } + return w.IssueUnsignedTx(utx, options...) +} + +func (w *wallet) IssueAddPermissionlessDelegatorTx( + vdr *validator.SubnetValidator, + assetID ids.ID, + rewardsOwner *secp256k1fx.OutputOwners, + options ...common.Option, +) (ids.ID, error) { + utx, err := w.builder.NewAddPermissionlessDelegatorTx( + vdr, + assetID, + rewardsOwner, + options..., + ) + if err != nil { + return ids.Empty, err + } + return w.IssueUnsignedTx(utx, options...) +} + func (w *wallet) IssueUnsignedTx( utx txs.UnsignedTx, options ...common.Option, diff --git a/avalanchego/wallet/chain/p/wallet_with_options.go b/avalanchego/wallet/chain/p/wallet_with_options.go index 4bb37e32..144d7707 100644 --- a/avalanchego/wallet/chain/p/wallet_with_options.go +++ b/avalanchego/wallet/chain/p/wallet_with_options.go @@ -1,11 +1,14 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package p import ( + "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/validator" "github.com/ava-labs/avalanchego/vms/secp256k1fx" @@ -70,6 +73,18 @@ func (w *walletWithOptions) IssueAddSubnetValidatorTx( ) } +func (w *walletWithOptions) IssueRemoveSubnetValidatorTx( + nodeID ids.NodeID, + subnetID ids.ID, + options ...common.Option, +) (ids.ID, error) { + return w.Wallet.IssueRemoveSubnetValidatorTx( + nodeID, + subnetID, + common.UnionOptions(w.options, options)..., + ) +} + func (w *walletWithOptions) IssueAddDelegatorTx( vdr *validator.Validator, rewardsOwner *secp256k1fx.OutputOwners, @@ -134,6 +149,76 @@ func (w *walletWithOptions) IssueExportTx( ) } +func (w *walletWithOptions) IssueTransformSubnetTx( + subnetID ids.ID, + assetID ids.ID, + initialSupply uint64, + maxSupply uint64, + minConsumptionRate uint64, + maxConsumptionRate uint64, + minValidatorStake uint64, + maxValidatorStake uint64, + minStakeDuration time.Duration, + maxStakeDuration time.Duration, + minDelegationFee uint32, + minDelegatorStake uint64, + maxValidatorWeightFactor byte, + uptimeRequirement uint32, + options ...common.Option, +) (ids.ID, error) { + return w.Wallet.IssueTransformSubnetTx( + subnetID, + assetID, + initialSupply, + maxSupply, + minConsumptionRate, + maxConsumptionRate, + minValidatorStake, + maxValidatorStake, + minStakeDuration, + maxStakeDuration, + minDelegationFee, + minDelegatorStake, + maxValidatorWeightFactor, + uptimeRequirement, + common.UnionOptions(w.options, options)..., + ) +} + +func (w *walletWithOptions) IssueAddPermissionlessValidatorTx( + vdr *validator.SubnetValidator, + signer signer.Signer, + assetID ids.ID, + validationRewardsOwner *secp256k1fx.OutputOwners, + delegationRewardsOwner *secp256k1fx.OutputOwners, + shares uint32, + options ...common.Option, +) (ids.ID, error) { + return w.Wallet.IssueAddPermissionlessValidatorTx( + vdr, + signer, + assetID, + validationRewardsOwner, + delegationRewardsOwner, + shares, + common.UnionOptions(w.options, options)..., + ) +} + +func (w *walletWithOptions) IssueAddPermissionlessDelegatorTx( + vdr *validator.SubnetValidator, + assetID ids.ID, + rewardsOwner *secp256k1fx.OutputOwners, + options ...common.Option, +) (ids.ID, error) { + return w.Wallet.IssueAddPermissionlessDelegatorTx( + vdr, + assetID, + rewardsOwner, + common.UnionOptions(w.options, options)..., + ) +} + func (w *walletWithOptions) IssueUnsignedTx( utx txs.UnsignedTx, options ...common.Option, diff --git a/avalanchego/wallet/chain/x/backend.go b/avalanchego/wallet/chain/x/backend.go index 64cfb09f..6b5c9481 100644 --- a/avalanchego/wallet/chain/x/backend.go +++ b/avalanchego/wallet/chain/x/backend.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x diff --git a/avalanchego/wallet/chain/x/builder.go b/avalanchego/wallet/chain/x/builder.go index d4f60ee8..d8f99602 100644 --- a/avalanchego/wallet/chain/x/builder.go +++ b/avalanchego/wallet/chain/x/builder.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x @@ -161,10 +161,10 @@ type builder struct { // NewBuilder returns a new transaction builder. // -// - [addrs] is the set of addresses that the builder assumes can be used when -// signing the transactions in the future. -// - [backend] provides the required access to the chain's context and state to -// build out the transactions. +// - [addrs] is the set of addresses that the builder assumes can be used when +// signing the transactions in the future. +// - [backend] provides the required access to the chain's context and state +// to build out the transactions. func NewBuilder(addrs ids.ShortSet, backend BuilderBackend) Builder { return &builder{ addrs: addrs, @@ -269,7 +269,7 @@ func (b *builder) NewOperationTx( options ...common.Option, ) (*txs.OperationTx, error) { toBurn := map[ids.ID]uint64{ - b.backend.AVAXAssetID(): b.backend.CreateAssetTxFee(), + b.backend.AVAXAssetID(): b.backend.BaseTxFee(), } ops := common.NewOptions(options) inputs, outputs, err := b.spend(toBurn, ops) diff --git a/avalanchego/wallet/chain/x/builder_with_options.go b/avalanchego/wallet/chain/x/builder_with_options.go index 846250aa..2dd098fe 100644 --- a/avalanchego/wallet/chain/x/builder_with_options.go +++ b/avalanchego/wallet/chain/x/builder_with_options.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x diff --git a/avalanchego/wallet/chain/x/constants.go b/avalanchego/wallet/chain/x/constants.go index 1ef64520..6ac6e0e1 100644 --- a/avalanchego/wallet/chain/x/constants.go +++ b/avalanchego/wallet/chain/x/constants.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x diff --git a/avalanchego/wallet/chain/x/context.go b/avalanchego/wallet/chain/x/context.go index 2814e4ca..7f081828 100644 --- a/avalanchego/wallet/chain/x/context.go +++ b/avalanchego/wallet/chain/x/context.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x diff --git a/avalanchego/wallet/chain/x/signer.go b/avalanchego/wallet/chain/x/signer.go index b60b2855..ad7ce762 100644 --- a/avalanchego/wallet/chain/x/signer.go +++ b/avalanchego/wallet/chain/x/signer.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x diff --git a/avalanchego/wallet/chain/x/wallet.go b/avalanchego/wallet/chain/x/wallet.go index 54d24719..4bbde834 100644 --- a/avalanchego/wallet/chain/x/wallet.go +++ b/avalanchego/wallet/chain/x/wallet.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x diff --git a/avalanchego/wallet/chain/x/wallet_with_options.go b/avalanchego/wallet/chain/x/wallet_with_options.go index a0d2b841..64be4e20 100644 --- a/avalanchego/wallet/chain/x/wallet_with_options.go +++ b/avalanchego/wallet/chain/x/wallet_with_options.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package x diff --git a/avalanchego/wallet/subnet/primary/api.go b/avalanchego/wallet/subnet/primary/api.go index 3049944e..4ec361af 100644 --- a/avalanchego/wallet/subnet/primary/api.go +++ b/avalanchego/wallet/subnet/primary/api.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package primary diff --git a/avalanchego/wallet/subnet/primary/common/options.go b/avalanchego/wallet/subnet/primary/common/options.go index 8aef796a..1d2ba7c1 100644 --- a/avalanchego/wallet/subnet/primary/common/options.go +++ b/avalanchego/wallet/subnet/primary/common/options.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/wallet/subnet/primary/common/spend.go b/avalanchego/wallet/subnet/primary/common/spend.go index ac1d0829..0f08ba41 100644 --- a/avalanchego/wallet/subnet/primary/common/spend.go +++ b/avalanchego/wallet/subnet/primary/common/spend.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package common diff --git a/avalanchego/wallet/subnet/primary/example_test.go b/avalanchego/wallet/subnet/primary/example_test.go index 0d94fbd1..441a0841 100644 --- a/avalanchego/wallet/subnet/primary/example_test.go +++ b/avalanchego/wallet/subnet/primary/example_test.go @@ -1,11 +1,11 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package primary import ( "context" - "fmt" + "log" "time" "github.com/ava-labs/avalanchego/genesis" @@ -14,6 +14,9 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/validator" "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) @@ -26,10 +29,10 @@ func ExampleWallet() { walletSyncStartTime := time.Now() wallet, err := NewWalletFromURI(ctx, LocalAPIURI, kc) if err != nil { - fmt.Printf("failed to initialize wallet with: %s\n", err) + log.Fatalf("failed to initialize wallet with: %s\n", err) return } - fmt.Printf("synced wallet in %s\n", time.Since(walletSyncStartTime)) + log.Printf("synced wallet in %s\n", time.Since(walletSyncStartTime)) // Get the P-chain and the X-chain wallets pWallet := wallet.P() @@ -53,19 +56,19 @@ func ExampleWallet() { map[uint32][]verify.State{ 0: { &secp256k1fx.TransferOutput{ - Amt: 100 * units.Schmeckle, + Amt: 100 * units.MegaAvax, OutputOwners: *owner, }, }, }, ) if err != nil { - fmt.Printf("failed to create new X-chain asset with: %s\n", err) + log.Fatalf("failed to create new X-chain asset with: %s\n", err) return } - fmt.Printf("created X-chain asset %s in %s\n", createAssetTxID, time.Since(createAssetStartTime)) + log.Printf("created X-chain asset %s in %s\n", createAssetTxID, time.Since(createAssetStartTime)) - // Send 100 schmeckles to the P-chain. + // Send 100 MegaAvax to the P-chain. exportStartTime := time.Now() exportTxID, err := xWallet.IssueExportTx( constants.PlatformChainID, @@ -75,55 +78,99 @@ func ExampleWallet() { ID: createAssetTxID, }, Out: &secp256k1fx.TransferOutput{ - Amt: 100 * units.Schmeckle, + Amt: 100 * units.MegaAvax, OutputOwners: *owner, }, }, }, ) if err != nil { - fmt.Printf("failed to issue X->P export transaction with: %s\n", err) + log.Fatalf("failed to issue X->P export transaction with: %s\n", err) return } - fmt.Printf("issued X->P export %s in %s\n", exportTxID, time.Since(exportStartTime)) + log.Printf("issued X->P export %s in %s\n", exportTxID, time.Since(exportStartTime)) - // Import the 100 schmeckles from the X-chain into the P-chain. + // Import the 100 MegaAvax from the X-chain into the P-chain. importStartTime := time.Now() importTxID, err := pWallet.IssueImportTx(xChainID, owner) if err != nil { - fmt.Printf("failed to issue X->P import transaction with: %s\n", err) + log.Fatalf("failed to issue X->P import transaction with: %s\n", err) return } - fmt.Printf("issued X->P import %s in %s\n", importTxID, time.Since(importStartTime)) + log.Printf("issued X->P import %s in %s\n", importTxID, time.Since(importStartTime)) - // Send 100 schmeckles back to the X-chain. - exportStartTime = time.Now() - exportTxID, err = pWallet.IssueExportTx( - xChainID, - []*avax.TransferableOutput{ - { - Asset: avax.Asset{ - ID: createAssetTxID, - }, - Out: &secp256k1fx.TransferOutput{ - Amt: 100 * units.Schmeckle, - OutputOwners: *owner, - }, + createSubnetStartTime := time.Now() + createSubnetTxID, err := pWallet.IssueCreateSubnetTx(owner) + if err != nil { + log.Fatalf("failed to issue create subnet transaction with: %s\n", err) + return + } + log.Printf("issued create subnet transaction %s in %s\n", createSubnetTxID, time.Since(createSubnetStartTime)) + + transformSubnetStartTime := time.Now() + transformSubnetTxID, err := pWallet.IssueTransformSubnetTx( + createSubnetTxID, + createAssetTxID, + 50*units.MegaAvax, + 100*units.MegaAvax, + reward.PercentDenominator, + reward.PercentDenominator, + 1, + 100*units.MegaAvax, + time.Second, + 365*24*time.Hour, + 0, + 1, + 5, + .80*reward.PercentDenominator, + ) + if err != nil { + log.Fatalf("failed to issue transform subnet transaction with: %s\n", err) + return + } + log.Printf("issued transform subnet transaction %s in %s\n", transformSubnetTxID, time.Since(transformSubnetStartTime)) + + addPermissionlessValidatorStartTime := time.Now() + startTime := time.Now().Add(time.Minute) + addSubnetValidatorTxID, err := pWallet.IssueAddPermissionlessValidatorTx( + &validator.SubnetValidator{ + Validator: validator.Validator{ + NodeID: genesis.LocalConfig.InitialStakers[0].NodeID, + Start: uint64(startTime.Unix()), + End: uint64(startTime.Add(5 * time.Second).Unix()), + Wght: 25 * units.MegaAvax, }, + Subnet: createSubnetTxID, }, + &signer.Empty{}, + createAssetTxID, + &secp256k1fx.OutputOwners{}, + &secp256k1fx.OutputOwners{}, + reward.PercentDenominator, ) if err != nil { - fmt.Printf("failed to issue P->X export transaction with: %s\n", err) + log.Fatalf("failed to issue add subnet validator with: %s\n", err) return } - fmt.Printf("issued P->X export %s in %s\n", exportTxID, time.Since(exportStartTime)) + log.Printf("issued add subnet validator transaction %s in %s\n", addSubnetValidatorTxID, time.Since(addPermissionlessValidatorStartTime)) - // Import the 100 schmeckles from the P-chain into the X-chain. - importStartTime = time.Now() - importTxID, err = xWallet.IssueImportTx(constants.PlatformChainID, owner) + addPermissionlessDelegatorStartTime := time.Now() + addSubnetDelegatorTxID, err := pWallet.IssueAddPermissionlessDelegatorTx( + &validator.SubnetValidator{ + Validator: validator.Validator{ + NodeID: genesis.LocalConfig.InitialStakers[0].NodeID, + Start: uint64(startTime.Unix()), + End: uint64(startTime.Add(5 * time.Second).Unix()), + Wght: 25 * units.MegaAvax, + }, + Subnet: createSubnetTxID, + }, + createAssetTxID, + &secp256k1fx.OutputOwners{}, + ) if err != nil { - fmt.Printf("failed to issue P->X import transaction with: %s\n", err) + log.Fatalf("failed to issue add subnet delegator with: %s\n", err) return } - fmt.Printf("issued P->X import %s in %s\n", importTxID, time.Since(importStartTime)) + log.Printf("issued add subnet validator delegator %s in %s\n", addSubnetDelegatorTxID, time.Since(addPermissionlessDelegatorStartTime)) } diff --git a/avalanchego/wallet/subnet/primary/utxos.go b/avalanchego/wallet/subnet/primary/utxos.go index d9732f01..365aae95 100644 --- a/avalanchego/wallet/subnet/primary/utxos.go +++ b/avalanchego/wallet/subnet/primary/utxos.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package primary diff --git a/avalanchego/wallet/subnet/primary/wallet.go b/avalanchego/wallet/subnet/primary/wallet.go index e7596d29..323197c5 100644 --- a/avalanchego/wallet/subnet/primary/wallet.go +++ b/avalanchego/wallet/subnet/primary/wallet.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package primary diff --git a/coreth/RELEASES.md b/coreth/RELEASES.md index d60002cd..d004d833 100644 --- a/coreth/RELEASES.md +++ b/coreth/RELEASES.md @@ -1,5 +1,16 @@ # Release Notes +## [v0.10.0](https://github.com/ava-labs/coreth/releases/tag/v0.10.0) + +- Deprecate Native Asset Call and Native Asset Balance +- Deprecate Import/Export of non-AVAX Avalanche Native Tokens via Atomic Transactions +- Add failure reason to bad block API + +## [v0.9.0](https://github.com/ava-labs/coreth/releases/tag/v0.9.0) + +- Migrate to go-ethereum v1.10.23 +- Add API to fetch Chain Config + ## [v0.8.16](https://github.com/ava-labs/coreth/releases/tag/v0.8.16) - Fix bug in `codeToFetch` database accessors that caused an error when starting/stopping state sync diff --git a/coreth/accounts/abi/abi.go b/coreth/accounts/abi/abi.go index b9f1863b..66dadac2 100644 --- a/coreth/accounts/abi/abi.go +++ b/coreth/accounts/abi/abi.go @@ -105,7 +105,7 @@ func (abi ABI) getArguments(name string, data []byte) (Arguments, error) { args = event.Inputs } if args == nil { - return nil, errors.New("abi: could not locate named method or event") + return nil, fmt.Errorf("abi: could not locate named method or event: %s", name) } return args, nil } diff --git a/coreth/accounts/abi/argument.go b/coreth/accounts/abi/argument.go index 4886c841..6245ca62 100644 --- a/coreth/accounts/abi/argument.go +++ b/coreth/accounts/abi/argument.go @@ -197,6 +197,9 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) { virtualArgs := 0 for index, arg := range nonIndexedArgs { marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data) + if err != nil { + return nil, err + } if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) { // If we have a static array, like [3]uint256, these are coded as // just like uint256,uint256,uint256. @@ -214,9 +217,6 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) { // coded as just like uint256,bool,uint256 virtualArgs += getTypeSize(arg.Type)/32 - 1 } - if err != nil { - return nil, err - } retval = append(retval, marshalledValue) } return retval, nil diff --git a/coreth/accounts/abi/bind/backends/simulated.go b/coreth/accounts/abi/bind/backends/simulated.go index 3922756e..38aa5ad2 100644 --- a/coreth/accounts/abi/bind/backends/simulated.go +++ b/coreth/accounts/abi/bind/backends/simulated.go @@ -97,7 +97,8 @@ type SimulatedBackend struct { acceptedBlock *types.Block // Currently accepted block that will be imported on request acceptedState *state.StateDB // Currently accepted state that will be the active on request - events *filters.EventSystem // Event system for filtering log events live + events *filters.EventSystem // for filtering log events live + filterSystem *filters.FilterSystem // for filtering database logs config *params.ChainConfig } @@ -118,7 +119,11 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis blockchain: blockchain, config: genesis.Config, } - backend.events = filters.NewEventSystem(&filterBackend{database, blockchain, backend}, false) + + filterBackend := &filterBackend{database, blockchain, backend} + backend.filterSystem = filters.NewFilterSystem(filterBackend, filters.Config{}) + backend.events = filters.NewEventSystem(backend.filterSystem, false) + backend.rollback(blockchain.CurrentBlock()) return backend } @@ -647,7 +652,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call interfaces.Cal // User specified the legacy gas field, convert to 1559 gas typing call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice } else { - // User specified 1559 gas feilds (or none), use those + // User specified 1559 gas fields (or none), use those if call.GasFeeCap == nil { call.GasFeeCap = new(big.Int) } @@ -729,7 +734,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query interfaces.Filt var filter *filters.Filter if query.BlockHash != nil { // Block filter requested, construct a single-shot filter - filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain, b}, *query.BlockHash, query.Addresses, query.Topics) + filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics) } else { // Initialize unset filter boundaries to run from genesis to chain head from := int64(0) @@ -741,7 +746,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query interfaces.Filt to = query.ToBlock.Int64() } // Construct the range filter - filter, _ = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain, b}, from, to, query.Addresses, query.Topics) + filter, _ = b.filterSystem.NewRangeFilter(from, to, query.Addresses, query.Topics) } // Run the filter and return all the logs logs, err := filter.Logs(ctx) @@ -891,7 +896,8 @@ func (fb *filterBackend) GetMaxBlocksPerRequest() int64 { return eth.DefaultSettings.MaxBlocksPerRequest } -func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db } +func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db } + func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") } func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) { @@ -913,19 +919,8 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil } -func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) { - number := rawdb.ReadHeaderNumber(fb.db, hash) - if number == nil { - return nil, nil - } - receipts := rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()) - if receipts == nil { - return nil, nil - } - logs := make([][]*types.Log, len(receipts)) - for i, receipt := range receipts { - logs[i] = receipt.Logs - } +func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { + logs := rawdb.ReadLogs(fb.db, hash, number) return logs, nil } diff --git a/coreth/accounts/abi/reflect.go b/coreth/accounts/abi/reflect.go index 3a49cd07..07bada39 100644 --- a/coreth/accounts/abi/reflect.go +++ b/coreth/accounts/abi/reflect.go @@ -109,7 +109,7 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value { func set(dst, src reflect.Value) error { dstType, srcType := dst.Type(), src.Type() switch { - case dstType.Kind() == reflect.Interface && dst.Elem().IsValid(): + case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()): return set(dst.Elem(), src) case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}): return set(dst.Elem(), src) diff --git a/coreth/accounts/abi/reflect_test.go b/coreth/accounts/abi/reflect_test.go index 2754fea6..f11cc113 100644 --- a/coreth/accounts/abi/reflect_test.go +++ b/coreth/accounts/abi/reflect_test.go @@ -42,7 +42,7 @@ type reflectTest struct { var reflectTests = []reflectTest{ { - name: "OneToOneCorrespondance", + name: "OneToOneCorrespondence", args: []string{"fieldA"}, struc: struct { FieldA int `abi:"fieldA"` diff --git a/coreth/accounts/abi/unpack.go b/coreth/accounts/abi/unpack.go index 27f9fd4e..0bab50f7 100644 --- a/coreth/accounts/abi/unpack.go +++ b/coreth/accounts/abi/unpack.go @@ -172,6 +172,9 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) { virtualArgs := 0 for index, elem := range t.TupleElems { marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output) + if err != nil { + return nil, err + } if elem.T == ArrayTy && !isDynamicType(*elem) { // If we have a static array, like [3]uint256, these are coded as // just like uint256,uint256,uint256. @@ -189,9 +192,6 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) { // coded as just like uint256,bool,uint256 virtualArgs += getTypeSize(*elem)/32 - 1 } - if err != nil { - return nil, err - } retval.Field(index).Set(reflect.ValueOf(marshalledValue)) } return retval.Interface(), nil diff --git a/coreth/accounts/abi/unpack_test.go b/coreth/accounts/abi/unpack_test.go index a71f38ce..e5e400ec 100644 --- a/coreth/accounts/abi/unpack_test.go +++ b/coreth/accounts/abi/unpack_test.go @@ -362,6 +362,11 @@ func TestMethodMultiReturn(t *testing.T) { &[]interface{}{&expected.Int, &expected.String}, "", "Can unpack into a slice", + }, { + &[]interface{}{&bigint, ""}, + &[]interface{}{&expected.Int, expected.String}, + "", + "Can unpack into a slice without indirection", }, { &[2]interface{}{&bigint, new(string)}, &[2]interface{}{&expected.Int, &expected.String}, diff --git a/coreth/accounts/keystore/account_cache_test.go b/coreth/accounts/keystore/account_cache_test.go index eb0d012e..c96c1dbd 100644 --- a/coreth/accounts/keystore/account_cache_test.go +++ b/coreth/accounts/keystore/account_cache_test.go @@ -329,7 +329,7 @@ func TestUpdatedKeyfileContents(t *testing.T) { t.Skip("FLAKY") t.Parallel() - // Create a temporary kesytore to test with + // Create a temporary keystore to test with rand.Seed(time.Now().UnixNano()) dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int())) ks := NewKeyStore(dir, LightScryptN, LightScryptP) diff --git a/coreth/accounts/keystore/file_cache.go b/coreth/accounts/keystore/file_cache.go index 25355de5..24dba599 100644 --- a/coreth/accounts/keystore/file_cache.go +++ b/coreth/accounts/keystore/file_cache.go @@ -49,7 +49,7 @@ type fileCache struct { func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) { t0 := time.Now() - // List all the failes from the keystore folder + // List all the files from the keystore folder files, err := os.ReadDir(keyDir) if err != nil { return nil, nil, nil, err @@ -71,7 +71,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er log.Trace("Ignoring file on account scan", "path", path) continue } - // Gather the set of all and fresly modified files + // Gather the set of all and freshly modified files all.Add(path) info, err := fi.Info() diff --git a/coreth/accounts/keystore/keystore_test.go b/coreth/accounts/keystore/keystore_test.go index d021264b..0a778647 100644 --- a/coreth/accounts/keystore/keystore_test.go +++ b/coreth/accounts/keystore/keystore_test.go @@ -224,7 +224,7 @@ func TestSignRace(t *testing.T) { // Tests that the wallet notifier loop starts and stops correctly based on the // addition and removal of wallet event subscriptions. func TestWalletNotifierLifecycle(t *testing.T) { - // Create a temporary kesytore to test with + // Create a temporary keystore to test with _, ks := tmpKeyStore(t, false) // Ensure that the notification updater is not running yet diff --git a/coreth/consensus/dummy/README.md b/coreth/consensus/dummy/README.md index dc4eabba..4edff330 100644 --- a/coreth/consensus/dummy/README.md +++ b/coreth/consensus/dummy/README.md @@ -12,7 +12,7 @@ The dummy consensus engine is responsible for performing verification on the hea ## Dynamic Fees -As of Apricot Phase 3, the C-Chain includes a dynamic fee algorithm based off of (EIP-1559)[]. This introduces a field to the block type called `BaseFee`. The Base Fee sets a minimum gas price for any transaction to be included in the block. For example, a transaction with a gas price of 49 gwei, will be invalid to include in a block with a base fee of 50 gwei. +As of Apricot Phase 3, the C-Chain includes a dynamic fee algorithm based off of (EIP-1559)[https://eips.ethereum.org/EIPS/eip-1559]. This introduces a field to the block type called `BaseFee`. The Base Fee sets a minimum gas price for any transaction to be included in the block. For example, a transaction with a gas price of 49 gwei, will be invalid to include in a block with a base fee of 50 gwei. The dynamic fee algorithm aims to adjust the base fee to handle network congestion. Coreth sets a target utilization on the network, and the dynamic fee algorithm adjusts the base fee accordingly. If the network operates above the target utilization, the dynamic fee algorithm will increase the base fee to make utilizing he network more expensive and bring overall utilization down. If the network operates below the target utilization, the dynamic fee algorithm will decrease the base fee to make it cheaper to use the network. diff --git a/coreth/core/blockchain.go b/coreth/core/blockchain.go index e5b1adce..3d8c0f08 100644 --- a/coreth/core/blockchain.go +++ b/coreth/core/blockchain.go @@ -58,6 +58,7 @@ var ( acceptorQueueGauge = metrics.NewRegisteredGauge("blockchain/acceptor/queue/size", nil) processedBlockGasUsedCounter = metrics.NewRegisteredCounter("blockchain/blocks/gas/used/processed", nil) acceptedBlockGasUsedCounter = metrics.NewRegisteredCounter("blockchain/blocks/gas/used/accepted", nil) + badBlockCounter = metrics.NewRegisteredCounter("blockchain/blocks/bad/count", nil) ErrRefuseToCorruptArchiver = errors.New("node has operated with pruning disabled, shutting down to prevent missing tries") @@ -535,6 +536,13 @@ func (bc *BlockChain) Export(w io.Writer) error { // ExportN writes a subset of the active chain to the given writer. func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { + return bc.ExportCallback(func(block *types.Block) error { + return block.EncodeRLP(w) + }, first, last) +} + +// ExportCallback invokes [callback] for every block from [first] to [last] in order. +func (bc *BlockChain) ExportCallback(callback func(block *types.Block) error, first uint64, last uint64) error { if first > last { return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) } @@ -554,7 +562,7 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { return fmt.Errorf("export failed: chain reorg during export") } parentHash = block.Hash() - if err := block.EncodeRLP(w); err != nil { + if err := callback(block); err != nil { return err } if time.Since(reported) >= statsReportLimit { @@ -703,6 +711,10 @@ func (bc *BlockChain) Stop() { log.Error("Failed to Shutdown state manager", "err", err) } log.Info("State manager shut down", "t", time.Since(start)) + // Flush the collected preimages to disk + if err := bc.stateCache.TrieDB().CommitPreimages(); err != nil { + log.Error("Failed to commit trie preimages", "err", err) + } // Stop senderCacher's goroutines log.Info("Shutting down sender cacher") @@ -877,7 +889,7 @@ func (bc *BlockChain) newTip(block *types.Block) bool { // writeBlockAndSetHead expects to be the last verification step during InsertBlock // since it creates a reference that will only be cleaned up by Accept/Reject. func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) error { - if err := bc.writeBlockWithState(block, receipts, logs, state); err != nil { + if err := bc.writeBlockWithState(block, receipts, state); err != nil { return err } @@ -894,7 +906,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types // writeBlockWithState writes the block and all associated state to the database, // but it expects the chain mutex to be held. -func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) error { +func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error { // Irrelevant of the canonical status, write the block itself to the database. // // Note all the components of block(hash->number map, header, body, receipts) @@ -1275,44 +1287,78 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { return nil } -// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network -func (bc *BlockChain) BadBlocks() []*types.Block { +type badBlock struct { + block *types.Block + reason *BadBlockReason +} + +type BadBlockReason struct { + ChainConfig *params.ChainConfig `json:"chainConfig"` + Receipts types.Receipts `json:"receipts"` + Number uint64 `json:"number"` + Hash common.Hash `json:"hash"` + Error error `json:"error"` +} + +func (b *BadBlockReason) String() string { + var receiptString string + for i, receipt := range b.Receipts { + receiptString += fmt.Sprintf("\t %d: cumulative: %v gas: %v contract: %v status: %v tx: %v logs: %v bloom: %x state: %x\n", + i, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.ContractAddress.Hex(), + receipt.Status, receipt.TxHash.Hex(), receipt.Logs, receipt.Bloom, receipt.PostState) + } + reason := fmt.Sprintf(` + ########## BAD BLOCK ######### + Chain config: %v + + Number: %v + Hash: %#x + %v + + Error: %v + ############################## + `, b.ChainConfig, b.Number, b.Hash, receiptString, b.Error) + + return reason +} + +// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network and the BadBlockReason +// that caused each to be reported as a bad block. +// BadBlocks ensures that the length of the blocks and the BadBlockReason slice have the same length. +func (bc *BlockChain) BadBlocks() ([]*types.Block, []*BadBlockReason) { blocks := make([]*types.Block, 0, bc.badBlocks.Len()) + reasons := make([]*BadBlockReason, 0, bc.badBlocks.Len()) for _, hash := range bc.badBlocks.Keys() { if blk, exist := bc.badBlocks.Peek(hash); exist { - block := blk.(*types.Block) - blocks = append(blocks, block) + badBlk := blk.(*badBlock) + blocks = append(blocks, badBlk.block) + reasons = append(reasons, badBlk.reason) } } - return blocks + return blocks, reasons } // addBadBlock adds a bad block to the bad-block LRU cache -func (bc *BlockChain) addBadBlock(block *types.Block) { - bc.badBlocks.Add(block.Hash(), block) +func (bc *BlockChain) addBadBlock(block *types.Block, reason *BadBlockReason) { + bc.badBlocks.Add(block.Hash(), &badBlock{ + block: block, + reason: reason, + }) } // reportBlock logs a bad block error. func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { - bc.addBadBlock(block) - - var receiptString string - for i, receipt := range receipts { - receiptString += fmt.Sprintf("\t %d: cumulative: %v gas: %v contract: %v status: %v tx: %v logs: %v bloom: %x state: %x\n", - i, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.ContractAddress.Hex(), - receipt.Status, receipt.TxHash.Hex(), receipt.Logs, receipt.Bloom, receipt.PostState) + reason := &BadBlockReason{ + ChainConfig: bc.chainConfig, + Receipts: receipts, + Number: block.NumberU64(), + Hash: block.Hash(), + Error: err, } - log.Error(fmt.Sprintf(` -########## BAD BLOCK ######### -Chain config: %v - -Number: %v -Hash: %#x -%v -Error: %v -############################## -`, bc.chainConfig, block.Number(), block.Hash(), receiptString, err)) + badBlockCounter.Inc(1) + bc.addBadBlock(block, reason) + log.Debug(reason.String()) } func (bc *BlockChain) RemoveRejectedBlocks(start, end uint64) error { @@ -1524,7 +1570,7 @@ func (bc *BlockChain) reprocessState(current *types.Block, reexec uint64) error // Flatten snapshot if initialized, holding a reference to the state root until the next block // is processed. if err := bc.flattenSnapshot(func() error { - triedb.Reference(root, common.Hash{}, true) + triedb.Reference(root, common.Hash{}) if previousRoot != (common.Hash{}) { triedb.Dereference(previousRoot) } diff --git a/coreth/core/blockchain_reader.go b/coreth/core/blockchain_reader.go index b5cdcc6c..e812fdd0 100644 --- a/coreth/core/blockchain_reader.go +++ b/coreth/core/blockchain_reader.go @@ -100,6 +100,9 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool { if bc.blockCache.Contains(hash) { return true } + if !bc.HasHeader(hash, number) { + return false + } return rawdb.HasBody(bc.db, hash, number) } diff --git a/coreth/core/bloom_indexer.go b/coreth/core/bloom_indexer.go index 4d8f58fd..60ac039e 100644 --- a/coreth/core/bloom_indexer.go +++ b/coreth/core/bloom_indexer.go @@ -75,7 +75,7 @@ func (b *BloomIndexer) Process(ctx context.Context, header *types.Header) error // Commit implements core.ChainIndexerBackend, finalizing the bloom section and // writing it out into the database. func (b *BloomIndexer) Commit() error { - batch := b.db.NewBatch() + batch := b.db.NewBatchWithSize((int(b.size) / 8) * types.BloomBitLength) for i := 0; i < types.BloomBitLength; i++ { bits, err := b.gen.Bitset(uint(i)) if err != nil { diff --git a/coreth/core/genesis.go b/coreth/core/genesis.go index 94453969..5c330ac3 100644 --- a/coreth/core/genesis.go +++ b/coreth/core/genesis.go @@ -162,7 +162,7 @@ func (e *GenesisMismatchError) Error() string { // The stored chain configuration will be updated if it is compatible (i.e. does not // specify a fork block below the local head block). In case of a conflict, the // error is a *params.ConfigCompatError and the new, unwritten config is returned. -func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, error) { +func SetupGenesisBlock(db ethdb.Database, genesis *Genesis, lastAcceptedHash common.Hash) (*params.ChainConfig, error) { if genesis == nil { return nil, ErrNoGenesis } @@ -209,12 +209,18 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig // Check config compatibility and write the config. Compatibility errors // are returned to the caller unless we're already at block zero. - headBlock := rawdb.ReadHeadBlock(db) - if headBlock == nil { - return newcfg, fmt.Errorf("missing head block") + // we use last accepted block for cfg compatibility check. Note this allows + // the node to continue if it previously halted due to attempting to process blocks with + // an incorrect chain config. + lastBlock := ReadBlockByHash(db, lastAcceptedHash) + // this should never happen, but we check anyway + // when we start syncing from scratch, the last accepted block + // will be genesis block + if lastBlock == nil { + return newcfg, fmt.Errorf("missing last accepted block") } - height := headBlock.NumberU64() - timestamp := headBlock.Time() + height := lastBlock.NumberU64() + timestamp := lastBlock.Time() compatErr := storedcfg.CheckCompatible(newcfg, height, timestamp) if compatErr != nil && height != 0 && compatErr.RewindTo != 0 { return newcfg, compatErr @@ -330,3 +336,12 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big } return g.MustCommit(db) } + +// ReadBlockByHash reads the block with the given hash from the database. +func ReadBlockByHash(db ethdb.Reader, hash common.Hash) *types.Block { + blockNumber := rawdb.ReadHeaderNumber(db, hash) + if blockNumber == nil { + return nil + } + return rawdb.ReadBlock(db, hash, *blockNumber) +} diff --git a/coreth/core/genesis_test.go b/coreth/core/genesis_test.go index 54e9ad03..ba1fa841 100644 --- a/coreth/core/genesis_test.go +++ b/coreth/core/genesis_test.go @@ -39,10 +39,11 @@ import ( "github.com/ava-labs/coreth/params" "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" ) -func setupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { - conf, err := SetupGenesisBlock(db, genesis) +func setupGenesisBlock(db ethdb.Database, genesis *Genesis, lastAcceptedHash common.Hash) (*params.ChainConfig, common.Hash, error) { + conf, err := SetupGenesisBlock(db, genesis, lastAcceptedHash) stored := rawdb.ReadCanonicalHash(db, 0) return conf, stored, err } @@ -82,7 +83,7 @@ func TestSetupGenesis(t *testing.T) { { name: "genesis without ChainConfig", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - return setupGenesisBlock(db, new(Genesis)) + return setupGenesisBlock(db, new(Genesis), common.Hash{}) }, wantErr: errGenesisNoConfig, wantConfig: nil, @@ -90,7 +91,7 @@ func TestSetupGenesis(t *testing.T) { { name: "no block in DB, genesis == nil", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - return setupGenesisBlock(db, nil) + return setupGenesisBlock(db, nil, common.Hash{}) }, wantErr: ErrNoGenesis, wantConfig: nil, @@ -99,7 +100,7 @@ func TestSetupGenesis(t *testing.T) { name: "custom block in DB, genesis == nil", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { customg.MustCommit(db) - return setupGenesisBlock(db, nil) + return setupGenesisBlock(db, nil, common.Hash{}) }, wantErr: ErrNoGenesis, wantHash: customghash, @@ -109,7 +110,7 @@ func TestSetupGenesis(t *testing.T) { name: "compatible config in DB", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { oldcustomg.MustCommit(db) - return setupGenesisBlock(db, &customg) + return setupGenesisBlock(db, &customg, customghash) }, wantHash: customghash, wantConfig: customg.Config, @@ -127,8 +128,14 @@ func TestSetupGenesis(t *testing.T) { blocks, _, _ := GenerateChain(oldcustomg.Config, genesis, dummy.NewFullFaker(), db, 4, 25, nil) bc.InsertChain(blocks) bc.CurrentBlock() + for _, block := range blocks { + if err := bc.Accept(block); err != nil { + t.Fatal(err) + } + } + // This should return a compatibility error. - return setupGenesisBlock(db, &customg) + return setupGenesisBlock(db, &customg, bc.lastAccepted.Hash()) }, wantHash: customghash, wantConfig: customg.Config, @@ -165,3 +172,54 @@ func TestSetupGenesis(t *testing.T) { }) } } + +// regression test for precompile activation after header block +func TestNetworkUpgradeBetweenHeadAndAcceptedBlock(t *testing.T) { + db := rawdb.NewMemoryDatabase() + + customg := Genesis{ + Config: params.TestApricotPhase1Config, + Alloc: GenesisAlloc{ + {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, + }, + } + genesis := customg.MustCommit(db) + bc, _ := NewBlockChain(db, DefaultCacheConfig, customg.Config, dummy.NewFullFaker(), vm.Config{}, common.Hash{}) + defer bc.Stop() + + // Advance header to block #4, past the ApricotPhase2 timestamp. + blocks, _, _ := GenerateChain(customg.Config, genesis, dummy.NewFullFaker(), db, 4, 25, nil) + + require := require.New(t) + _, err := bc.InsertChain(blocks) + require.NoError(err) + + // accept up to block #2 + for _, block := range blocks[:2] { + require.NoError(bc.Accept(block)) + } + block := bc.CurrentBlock() + + require.Equal(blocks[1].Hash(), bc.lastAccepted.Hash()) + // header must be bigger than last accepted + require.Greater(block.Time(), bc.lastAccepted.Time()) + + activatedGenesis := customg + apricotPhase2Timestamp := big.NewInt(51) + updatedApricotPhase2Config := *params.TestApricotPhase1Config + updatedApricotPhase2Config.ApricotPhase2BlockTimestamp = apricotPhase2Timestamp + + activatedGenesis.Config = &updatedApricotPhase2Config + + // assert block is after the activation block + require.Greater(block.Time(), apricotPhase2Timestamp.Uint64()) + // assert last accepted block is before the activation block + require.Less(bc.lastAccepted.Time(), apricotPhase2Timestamp.Uint64()) + + // This should not return any error since the last accepted block is before the activation block. + config, _, err := setupGenesisBlock(db, &activatedGenesis, bc.lastAccepted.Hash()) + require.NoError(err) + if !reflect.DeepEqual(config, activatedGenesis.Config) { + t.Errorf("returned %v\nwant %v", config, activatedGenesis.Config) + } +} diff --git a/coreth/core/rawdb/accessors_chain_test.go b/coreth/core/rawdb/accessors_chain_test.go index cf7b6465..9feb4c0d 100644 --- a/coreth/core/rawdb/accessors_chain_test.go +++ b/coreth/core/rawdb/accessors_chain_test.go @@ -191,7 +191,7 @@ func TestPartialBlockStorage(t *testing.T) { func TestCanonicalMappingStorage(t *testing.T) { db := NewMemoryDatabase() - // Create a test canonical number and assinged hash to move around + // Create a test canonical number and assigned hash to move around hash, number := common.Hash{0: 0xff}, uint64(314) if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) { t.Fatalf("Non existent canonical mapping returned: %v", entry) diff --git a/coreth/core/rawdb/accessors_state_sync.go b/coreth/core/rawdb/accessors_state_sync.go index 00cde550..4a16055f 100644 --- a/coreth/core/rawdb/accessors_state_sync.go +++ b/coreth/core/rawdb/accessors_state_sync.go @@ -6,7 +6,6 @@ package rawdb import ( "github.com/ava-labs/coreth/ethdb" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/log" ) @@ -47,7 +46,7 @@ func DeleteCodeToFetch(db ethdb.KeyValueWriter, hash common.Hash) { // hashes that are pending syncing. It is the caller's responsibility to // unpack the key and call Release on the returned iterator. func NewCodeToFetchIterator(db ethdb.Iteratee) ethdb.Iterator { - return rawdb.NewKeyLengthIterator( + return NewKeyLengthIterator( db.NewIterator(CodeToFetchPrefix, nil), codeToFetchKeyLength, ) @@ -68,7 +67,7 @@ func NewSyncSegmentsIterator(db ethdb.Iteratee, root common.Hash) ethdb.Iterator copy(segmentsPrefix, syncSegmentsPrefix) copy(segmentsPrefix[len(syncSegmentsPrefix):], root[:]) - return rawdb.NewKeyLengthIterator( + return NewKeyLengthIterator( db.NewIterator(segmentsPrefix, nil), syncSegmentsKeyLength, ) @@ -115,7 +114,7 @@ func packSyncSegmentKey(root common.Hash, start []byte) []byte { // added for syncing (beginning at seek). It is the caller's responsibility to unpack // the key and call Release on the returned iterator. func NewSyncStorageTriesIterator(db ethdb.Iteratee, seek []byte) ethdb.Iterator { - return rawdb.NewKeyLengthIterator(db.NewIterator(syncStorageTriesPrefix, seek), syncStorageTriesKeyLength) + return NewKeyLengthIterator(db.NewIterator(syncStorageTriesPrefix, seek), syncStorageTriesKeyLength) } // WriteSyncStorageTrie adds a storage trie for account (with the given root) to be synced. diff --git a/coreth/core/state/database.go b/coreth/core/state/database.go index bbe8770b..e861234d 100644 --- a/coreth/core/state/database.go +++ b/coreth/core/state/database.go @@ -73,7 +73,7 @@ type Trie interface { // GetKey returns the sha3 preimage of a hashed key that was previously used // to store a value. // - // TODO(fjl): remove this when SecureTrie is removed + // TODO(fjl): remove this when StateTrie is removed GetKey([]byte) []byte // TryGet returns the value for key stored in the trie. The value bytes must @@ -81,8 +81,8 @@ type Trie interface { // trie.MissingNodeError is returned. TryGet(key []byte) ([]byte, error) - // TryUpdateAccount abstract an account write in the trie. - TryUpdateAccount(key []byte, account *types.StateAccount) error + // TryGetAccount abstract an account read from the trie. + TryGetAccount(key []byte) (*types.StateAccount, error) // TryUpdate associates key with value in the trie. If value has length zero, any // existing value is deleted from the trie. The value bytes must not be modified @@ -90,17 +90,27 @@ type Trie interface { // database, a trie.MissingNodeError is returned. TryUpdate(key, value []byte) error + // TryUpdateAccount abstract an account write to the trie. + TryUpdateAccount(key []byte, account *types.StateAccount) error + // TryDelete removes any existing value for key from the trie. If a node was not // found in the database, a trie.MissingNodeError is returned. TryDelete(key []byte) error + // TryDeleteAccount abstracts an account deletion from the trie. + TryDeleteAccount(key []byte) error + // Hash returns the root hash of the trie. It does not write to the database and // can be used even if the trie doesn't have one. Hash() common.Hash - // Commit writes all nodes to the trie's memory database, tracking the internal - // and external (for account tries) references. - Commit(onleaf trie.LeafCallback, referenceRoot bool) (common.Hash, int, error) + // Commit collects all dirty nodes in the trie and replace them with the + // corresponding node hash. All collected nodes(including dirty leaves if + // collectLeaf is true) will be encapsulated into a nodeset for return. + // The returned nodeset can be nil if the trie is clean(nothing to commit). + // Once the trie is committed, it's not usable anymore. A new trie must + // be created with new root and updated trie database for following usage + Commit(collectLeaf bool) (common.Hash, *trie.NodeSet, error) // NodeIterator returns an iterator that returns nodes of the trie. Iteration // starts at the key after the given start key. @@ -143,7 +153,7 @@ type cachingDB struct { // OpenTrie opens the main account trie at a specific root hash. func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { - tr, err := trie.NewSecure(common.Hash{}, root, db.db) + tr, err := trie.NewStateTrie(common.Hash{}, root, db.db) if err != nil { return nil, err } @@ -152,7 +162,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { // OpenStorageTrie opens the storage trie of an account. func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { - tr, err := trie.NewSecure(addrHash, root, db.db) + tr, err := trie.NewStateTrie(addrHash, root, db.db) if err != nil { return nil, err } @@ -162,7 +172,7 @@ func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { // CopyTrie returns an independent copy of the given trie. func (db *cachingDB) CopyTrie(t Trie) Trie { switch t := t.(type) { - case *trie.SecureTrie: + case *trie.StateTrie: return t.Copy() default: panic(fmt.Errorf("unknown trie type %T", t)) diff --git a/coreth/core/state/metrics.go b/coreth/core/state/metrics.go index cc7838a7..6d702312 100644 --- a/coreth/core/state/metrics.go +++ b/coreth/core/state/metrics.go @@ -29,10 +29,10 @@ package state import "github.com/ava-labs/coreth/metrics" var ( - accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil) - storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil) - accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil) - storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil) - accountCommittedMeter = metrics.NewRegisteredMeter("state/commit/account", nil) - storageCommittedMeter = metrics.NewRegisteredMeter("state/commit/storage", nil) + accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil) + storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil) + accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil) + storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil) + accountTrieCommittedMeter = metrics.NewRegisteredMeter("state/commit/accountnodes", nil) + storageTriesCommittedMeter = metrics.NewRegisteredMeter("state/commit/storagenodes", nil) ) diff --git a/coreth/core/state/pruner/pruner.go b/coreth/core/state/pruner/pruner.go index 80118d63..d7301562 100644 --- a/coreth/core/state/pruner/pruner.go +++ b/coreth/core/state/pruner/pruner.go @@ -341,7 +341,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { if genesis == nil { return errors.New("missing genesis block") } - t, err := trie.NewSecure(common.Hash{}, genesis.Root(), trie.NewDatabase(db)) + t, err := trie.NewStateTrie(common.Hash{}, genesis.Root(), trie.NewDatabase(db)) if err != nil { return err } @@ -361,7 +361,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { return err } if acc.Root != emptyRoot { - storageTrie, err := trie.NewSecure(common.BytesToHash(accIter.LeafKey()), acc.Root, trie.NewDatabase(db)) + storageTrie, err := trie.NewStateTrie(common.BytesToHash(accIter.LeafKey()), acc.Root, trie.NewDatabase(db)) if err != nil { return err } diff --git a/coreth/core/state/snapshot/generate.go b/coreth/core/state/snapshot/generate.go index f052fe3c..7362e530 100644 --- a/coreth/core/state/snapshot/generate.go +++ b/coreth/core/state/snapshot/generate.go @@ -274,7 +274,7 @@ func (dl *diskLayer) generate(stats *generatorStats) { } } // Create an account and state iterator pointing to the current generator marker - accTrie, err := trie.NewSecure(common.Hash{}, dl.root, dl.triedb) + accTrie, err := trie.NewStateTrie(common.Hash{}, dl.root, dl.triedb) if err != nil { // The account trie is missing (GC), surf the chain until one becomes available stats.Info("Trie missing, state snapshotting paused", dl.root, dl.genMarker) @@ -329,7 +329,7 @@ func (dl *diskLayer) generate(stats *generatorStats) { // If the iterated account is a contract, iterate through corresponding contract // storage to generate snapshot entries. if acc.Root != emptyRoot { - storeTrie, err := trie.NewSecure(accountHash, acc.Root, dl.triedb) + storeTrie, err := trie.NewStateTrie(accountHash, acc.Root, dl.triedb) if err != nil { log.Error("Generator failed to access storage trie", "root", dl.root, "account", accountHash, "stroot", acc.Root, "err", err) abort := <-dl.genAbort diff --git a/coreth/core/state/snapshot/generate_test.go b/coreth/core/state/snapshot/generate_test.go index ed3eb5cd..b1ab8a49 100644 --- a/coreth/core/state/snapshot/generate_test.go +++ b/coreth/core/state/snapshot/generate_test.go @@ -152,17 +152,19 @@ func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) { type testHelper struct { diskdb ethdb.Database triedb *trie.Database - accTrie *trie.SecureTrie + accTrie *trie.StateTrie + nodes *trie.MergedNodeSet } func newHelper() *testHelper { diskdb := rawdb.NewMemoryDatabase() triedb := trie.NewDatabase(diskdb) - accTrie, _ := trie.NewSecure(common.Hash{}, common.Hash{}, triedb) + accTrie, _ := trie.NewStateTrie(common.Hash{}, common.Hash{}, triedb) return &testHelper{ diskdb: diskdb, triedb: triedb, accTrie: accTrie, + nodes: trie.NewMergedNodeSet(), } } @@ -190,21 +192,26 @@ func (t *testHelper) addSnapStorage(accKey string, keys []string, vals []string) } func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []string, commit bool) []byte { - stTrie, _ := trie.NewSecure(owner, common.Hash{}, t.triedb) + stTrie, _ := trie.NewStateTrie(owner, common.Hash{}, t.triedb) for i, k := range keys { stTrie.Update([]byte(k), []byte(vals[i])) } - var root common.Hash if !commit { - root = stTrie.Hash() - } else { - root, _, _ = stTrie.Commit(nil, false) + return stTrie.Hash().Bytes() + } + root, nodes, _ := stTrie.Commit(false) + if nodes != nil { + t.nodes.Merge(nodes) } return root.Bytes() } func (t *testHelper) Commit() common.Hash { - root, _, _ := t.accTrie.Commit(nil, false) + root, nodes, _ := t.accTrie.Commit(true) + if nodes != nil { + t.nodes.Merge(nodes) + } + t.triedb.Update(t.nodes) t.triedb.Commit(root, false, nil) return root } @@ -223,12 +230,10 @@ func (t *testHelper) CommitAndGenerate() (common.Hash, *diskLayer) { // - miss in the beginning // - miss in the middle // - miss in the end -// // - the contract(non-empty storage) has wrong storage slots // - wrong slots in the beginning // - wrong slots in the middle // - wrong slots in the end -// // - the contract(non-empty storage) has extra storage slots // - extra slots in the beginning // - extra slots in the middle @@ -390,7 +395,7 @@ func TestGenerateCorruptAccountTrie(t *testing.T) { helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0xf73118e0254ce091588d66038744a0afae5f65a194de67cff310c683ae43329e helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x515d3de35e143cd976ad476398d910aa7bf8a02e8fd7eb9e3baacddbbcbfcb41 - root, _, _ := helper.accTrie.Commit(nil, false) // Root: 0xfa04f652e8bd3938971bf7d71c3c688574af334ca8bc20e64b01ba610ae93cad + root := helper.Commit() // Root: 0xfa04f652e8bd3938971bf7d71c3c688574af334ca8bc20e64b01ba610ae93cad // Delete an account trie leaf and ensure the generator chokes helper.triedb.Commit(root, false, nil) @@ -425,20 +430,7 @@ func TestGenerateMissingStorageTrie(t *testing.T) { helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0xf73118e0254ce091588d66038744a0afae5f65a194de67cff310c683ae43329e stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441 - root, _, _ := helper.accTrie.Commit(nil, false) - - // We can only corrupt the disk database, so flush the tries out - helper.triedb.Reference( - common.BytesToHash(stRoot), - common.HexToHash("0x547b07c3a71669c00eda14077d85c7fd14575b92d459572540b25b9a11914dcb"), - true, - ) - helper.triedb.Reference( - common.BytesToHash(stRoot), - common.HexToHash("0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441"), - true, - ) - helper.triedb.Commit(root, false, nil) + root := helper.Commit() // Delete a storage trie root and ensure the generator chokes helper.diskdb.Delete(stRoot) // We can only corrupt the disk database, so flush the tries out @@ -471,21 +463,7 @@ func TestGenerateCorruptStorageTrie(t *testing.T) { helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0xf73118e0254ce091588d66038744a0afae5f65a194de67cff310c683ae43329e stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441 - - root, _, _ := helper.accTrie.Commit(nil, false) - - // We can only corrupt the disk database, so flush the tries out - helper.triedb.Reference( - common.BytesToHash(stRoot), - common.HexToHash("0x547b07c3a71669c00eda14077d85c7fd14575b92d459572540b25b9a11914dcb"), - true, - ) - helper.triedb.Reference( - common.BytesToHash(stRoot), - common.HexToHash("0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441"), - true, - ) - helper.triedb.Commit(root, false, nil) + root := helper.Commit() // Delete a storage trie leaf and ensure the generator chokes helper.diskdb.Delete(common.HexToHash("0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371").Bytes()) @@ -836,10 +814,12 @@ func populateDangling(disk ethdb.KeyValueStore) { // This test will populate some dangling storages to see if they can be cleaned up. func TestGenerateCompleteSnapshotWithDanglingStorage(t *testing.T) { var helper = newHelper() - stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) + + helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) @@ -869,10 +849,12 @@ func TestGenerateCompleteSnapshotWithDanglingStorage(t *testing.T) { // This test will populate some dangling storages to see if they can be cleaned up. func TestGenerateBrokenSnapshotWithDanglingStorage(t *testing.T) { var helper = newHelper() - stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) + + helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) populateDangling(helper.diskdb) diff --git a/coreth/core/state/snapshot/iterator_fast.go b/coreth/core/state/snapshot/iterator_fast.go index 1011da35..bf0e3acd 100644 --- a/coreth/core/state/snapshot/iterator_fast.go +++ b/coreth/core/state/snapshot/iterator_fast.go @@ -328,7 +328,7 @@ func (fi *fastIterator) Slot() []byte { } // Release iterates over all the remaining live layer iterators and releases each -// of thme individually. +// of them individually. func (fi *fastIterator) Release() { for _, it := range fi.iterators { it.it.Release() @@ -336,7 +336,7 @@ func (fi *fastIterator) Release() { fi.iterators = nil } -// Debug is a convencience helper during testing +// Debug is a convenience helper during testing func (fi *fastIterator) Debug() { for _, it := range fi.iterators { fmt.Printf("[p=%v v=%v] ", it.priority, it.it.Hash()[0]) diff --git a/coreth/core/state/snapshot/snapshot_test.go b/coreth/core/state/snapshot/snapshot_test.go index 2daca9c8..03879c48 100644 --- a/coreth/core/state/snapshot/snapshot_test.go +++ b/coreth/core/state/snapshot/snapshot_test.go @@ -246,7 +246,7 @@ func TestPostFlattenBasicDataAccess(t *testing.T) { snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xffa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil) snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xffb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil) - // checkExist verifies if an account exiss in a snapshot + // checkExist verifies if an account exists in a snapshot checkExist := func(layer Snapshot, key string) error { if data, _ := layer.Account(common.HexToHash(key)); data == nil { return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key)) diff --git a/coreth/core/state/state_object.go b/coreth/core/state/state_object.go index 6a20a470..7d1ac913 100644 --- a/coreth/core/state/state_object.go +++ b/coreth/core/state/state_object.go @@ -36,6 +36,7 @@ import ( "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/coreth/metrics" + "github.com/ava-labs/coreth/trie" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" @@ -390,23 +391,23 @@ func (s *stateObject) updateRoot(db Database) { // CommitTrie the storage trie of the object to db. // This updates the trie root. -func (s *stateObject) CommitTrie(db Database) (int, error) { +func (s *stateObject) CommitTrie(db Database) (*trie.NodeSet, error) { // If nothing changed, don't bother with hashing anything if s.updateTrie(db) == nil { - return 0, nil + return nil, nil } if s.dbErr != nil { - return 0, s.dbErr + return nil, s.dbErr } // Track the amount of time wasted on committing the storage trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now()) } - root, committed, err := s.trie.Commit(nil, false) + root, nodes, err := s.trie.Commit(false) if err == nil { s.data.Root = root } - return committed, err + return nodes, err } // AddBalance adds amount to s's balance. diff --git a/coreth/core/state/statedb.go b/coreth/core/state/statedb.go index 4a5476bf..ca69d1bd 100644 --- a/coreth/core/state/statedb.go +++ b/coreth/core/state/statedb.go @@ -552,7 +552,7 @@ func (s *StateDB) deleteStateObject(obj *stateObject) { } // Delete the account from the trie addr := obj.Address() - if err := s.trie.TryDelete(addr[:]); err != nil { + if err := s.trie.TryDeleteAccount(addr[:]); err != nil { s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err)) } } @@ -606,20 +606,16 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { // If snapshot unavailable or reading from it failed, load from the database if data == nil { start := time.Now() - enc, err := s.trie.TryGet(addr.Bytes()) + var err error + data, err = s.trie.TryGetAccount(addr.Bytes()) if metrics.EnabledExpensive { s.AccountReads += time.Since(start) } if err != nil { - s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr.Bytes(), err)) + s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %w", addr.Bytes(), err)) return nil } - if len(enc) == 0 { - return nil - } - data = new(types.StateAccount) - if err := rlp.DecodeBytes(enc, data); err != nil { - log.Error("Failed to decode state object", "addr", addr, "err", err) + if data == nil { return nil } } @@ -842,7 +838,7 @@ func (s *StateDB) GetRefund() uint64 { return s.refund } -// Finalise finalises the state by removing the s destructed objects and clears +// Finalise finalises the state by removing the destructed objects and clears // the journal as well as the refunds. Finalise, however, will not push any updates // into the tries just yet. Only IntermediateRoot or Commit will do that. func (s *StateDB) Finalise(deleteEmptyObjects bool) { @@ -864,7 +860,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // If state snapshotting is active, also mark the destruction there. // Note, we can't do this only at the end of a block because multiple // transactions within the same block might self destruct and then - // ressurrect an account; but the snapshotter needs both events. + // resurrect an account; but the snapshotter needs both events. if s.snap != nil { s.snapDestructs[obj.addrHash] = struct{}{} // We need to maintain account deletions explicitly (will remain set indefinitely) delete(s.snapAccounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a ressurrect) @@ -912,7 +908,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Although naively it makes sense to retrieve the account trie and then do // the contract storage and account updates sequentially, that short circuits // the account prefetcher. Instead, let's process all the storage updates - // first, giving the account prefeches just a few more milliseconds of time + // first, giving the account prefetches just a few more milliseconds of time // to pull useful data from disk. for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { @@ -963,7 +959,7 @@ func (s *StateDB) clearJournalAndRefund() { s.journal = newJournal() s.refund = 0 } - s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entires + s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entries } // Commit writes the state to the underlying in-memory trie database. @@ -986,7 +982,11 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas s.IntermediateRoot(deleteEmptyObjects) // Commit objects to the trie, measuring the elapsed time - var storageCommitted int + var ( + accountTrieNodes int + storageTrieNodes int + nodes = trie.NewMergedNodeSet() + ) codeWriter := s.db.TrieDB().DiskDB().NewBatch() for addr := range s.stateObjectsDirty { if obj := s.stateObjects[addr]; !obj.deleted { @@ -996,11 +996,17 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas obj.dirtyCode = false } // Write any storage changes in the state object to its storage trie - committed, err := obj.CommitTrie(s.db) + set, err := obj.CommitTrie(s.db) if err != nil { return common.Hash{}, err } - storageCommitted += committed + // Merge the dirty nodes of storage trie into global set + if set != nil { + if err := nodes.Merge(set); err != nil { + return common.Hash{}, err + } + storageTrieNodes += set.Len() + } } } if len(s.stateObjectsDirty) > 0 { @@ -1016,21 +1022,17 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas if metrics.EnabledExpensive { start = time.Now() } - // The onleaf func is called _serially_, so we can reuse the same account - // for unmarshalling every time. - var account types.StateAccount - root, accountCommitted, err := s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash, _ []byte) error { - if err := rlp.DecodeBytes(leaf, &account); err != nil { - return nil - } - if account.Root != emptyRoot { - s.db.TrieDB().Reference(account.Root, parent, false) - } - return nil - }, referenceRoot) + root, set, err := s.trie.Commit(true) if err != nil { return common.Hash{}, err } + // Merge the dirty nodes of account trie into global set + if set != nil { + if err := nodes.Merge(set); err != nil { + return common.Hash{}, err + } + accountTrieNodes = set.Len() + } if metrics.EnabledExpensive { s.AccountCommits += time.Since(start) @@ -1038,8 +1040,8 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas storageUpdatedMeter.Mark(int64(s.StorageUpdated)) accountDeletedMeter.Mark(int64(s.AccountDeleted)) storageDeletedMeter.Mark(int64(s.StorageDeleted)) - accountCommittedMeter.Mark(int64(accountCommitted)) - storageCommittedMeter.Mark(int64(storageCommitted)) + accountTrieCommittedMeter.Mark(int64(accountTrieNodes)) + storageTriesCommittedMeter.Mark(int64(storageTrieNodes)) s.AccountUpdated, s.AccountDeleted = 0, 0 s.StorageUpdated, s.StorageDeleted = 0, 0 } @@ -1056,6 +1058,15 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas } s.snap, s.snapDestructs, s.snapAccounts, s.snapStorage = nil, nil, nil, nil } + if referenceRoot { + if err := s.db.TrieDB().UpdateAndReferenceRoot(nodes, root); err != nil { + return common.Hash{}, err + } + } else { + if err := s.db.TrieDB().Update(nodes); err != nil { + return common.Hash{}, err + } + } s.originalRoot = root return root, err } diff --git a/coreth/core/state/statedb_test.go b/coreth/core/state/statedb_test.go index eed36f73..c2156b2e 100644 --- a/coreth/core/state/statedb_test.go +++ b/coreth/core/state/statedb_test.go @@ -783,7 +783,7 @@ func TestStateDBAccessList(t *testing.T) { t.Fatalf("expected %x to be in access list", address) } } - // Check that only the expected addresses are present in the acesslist + // Check that only the expected addresses are present in the access list for address := range state.accessList.addresses { if _, exist := addressMap[address]; !exist { t.Fatalf("extra address %x in access list", address) @@ -1072,3 +1072,43 @@ func TestGenerateMultiCoinAccounts(t *testing.T) { t.Fatalf("Expected asset balance: %v, found %v", assetBalance, actualAssetBalance) } } + +// Tests that account and storage tries are flushed in the correct order and that +// no data loss occurs. +func TestFlushOrderDataLoss(t *testing.T) { + // Create a state trie with many accounts and slots + var ( + memdb = rawdb.NewMemoryDatabase() + statedb = NewDatabase(memdb) + state, _ = New(common.Hash{}, statedb, nil) + ) + for a := byte(0); a < 10; a++ { + state.CreateAccount(common.Address{a}) + for s := byte(0); s < 10; s++ { + state.SetState(common.Address{a}, common.Hash{a, s}, common.Hash{a, s}) + } + } + root, err := state.Commit(false, false) + if err != nil { + t.Fatalf("failed to commit state trie: %v", err) + } + statedb.TrieDB().Reference(root, common.Hash{}) + if err := statedb.TrieDB().Cap(1024); err != nil { + t.Fatalf("failed to cap trie dirty cache: %v", err) + } + if err := statedb.TrieDB().Commit(root, false, nil); err != nil { + t.Fatalf("failed to commit state trie: %v", err) + } + // Reopen the state trie from flushed disk and verify it + state, err = New(root, NewDatabase(memdb), nil) + if err != nil { + t.Fatalf("failed to reopen state trie: %v", err) + } + for a := byte(0); a < 10; a++ { + for s := byte(0); s < 10; s++ { + if have := state.GetState(common.Address{a}, common.Hash{a, s}); have != (common.Hash{a, s}) { + t.Errorf("account %d: slot %d: state mismatch: have %x, want %x", a, s, have, common.Hash{a, s}) + } + } + } +} diff --git a/coreth/core/state/trie_prefetcher.go b/coreth/core/state/trie_prefetcher.go index 349bc39e..5da0e9cc 100644 --- a/coreth/core/state/trie_prefetcher.go +++ b/coreth/core/state/trie_prefetcher.go @@ -222,7 +222,7 @@ type subfetcher struct { wake chan struct{} // Wake channel if a new task is scheduled stop chan struct{} // Channel to interrupt processing - term chan struct{} // Channel to signal iterruption + term chan struct{} // Channel to signal interruption copy chan chan Trie // Channel to request a copy of the current trie seen map[string]struct{} // Tracks the entries already loaded @@ -342,7 +342,12 @@ func (sf *subfetcher) loop() { if _, ok := sf.seen[string(task)]; ok { sf.dups++ } else { - _, err := sf.trie.TryGet(task) + var err error + if len(task) == len(common.Address{}) { + _, err = sf.trie.TryGetAccount(task) + } else { + _, err = sf.trie.TryGet(task) + } if err != nil { log.Error("Trie prefetcher failed fetching", "root", sf.root, "err", err) } diff --git a/coreth/core/state_processor.go b/coreth/core/state_processor.go index 535a39b6..c6874e31 100644 --- a/coreth/core/state_processor.go +++ b/coreth/core/state_processor.go @@ -94,7 +94,7 @@ func (p *StateProcessor) Process(block *types.Block, parent *types.Header, state return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) + receipt, err := applyTransaction(msg, p.config, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -109,7 +109,7 @@ func (p *StateProcessor) Process(block *types.Block, parent *types.Header, state return receipts, allLogs, *usedGas, nil } -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { +func applyTransaction(msg types.Message, config *params.ChainConfig, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -166,5 +166,5 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) + return applyTransaction(msg, config, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } diff --git a/coreth/core/state_processor_test.go b/coreth/core/state_processor_test.go index 0ea72527..1ca66bf3 100644 --- a/coreth/core/state_processor_test.go +++ b/coreth/core/state_processor_test.go @@ -36,9 +36,9 @@ import ( "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/coreth/core/vm" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/trie" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/trie" "golang.org/x/crypto/sha3" ) diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index 672e33f2..04ced226 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -29,6 +29,7 @@ package core import ( "bytes" "encoding/binary" + "errors" "fmt" "math" "math/big" @@ -394,7 +395,13 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } } } - + if errors.Is(vmerr, vmerrs.ErrToAddrProhibitedSoft) { // Only invalidate soft error here + return &ExecutionResult{ + UsedGas: st.gasUsed(), + Err: vmerr, + ReturnData: ret, + }, vmerr + } st.refundGas(rules.IsApricotPhase1) if vmerr == nil && IsPrioritisedContractCall(chainID, timestamp, msg.To(), ret, st.initialGas) { diff --git a/coreth/core/tx_journal.go b/coreth/core/tx_journal.go index 82d3c62f..05f59a30 100644 --- a/coreth/core/tx_journal.go +++ b/coreth/core/tx_journal.go @@ -29,6 +29,7 @@ package core import ( "errors" "io" + "io/fs" "os" "github.com/ava-labs/coreth/core/types" @@ -67,12 +68,12 @@ func newTxJournal(path string) *txJournal { // load parses a transaction journal dump from disk, loading its contents into // the specified pool. func (journal *txJournal) load(add func([]*types.Transaction) []error) error { - // Skip the parsing if the journal file doesn't exist at all - if !common.FileExist(journal.path) { - return nil - } // Open the journal for loading any past transactions input, err := os.Open(journal.path) + if errors.Is(err, fs.ErrNotExist) { + // Skip the parsing if the journal file doesn't exist at all + return nil + } if err != nil { return err } diff --git a/coreth/core/tx_pool.go b/coreth/core/tx_pool.go index 879cca73..ce39186a 100644 --- a/coreth/core/tx_pool.go +++ b/coreth/core/tx_pool.go @@ -1074,6 +1074,13 @@ func (pool *TxPool) HasLocal(hash common.Hash) bool { return pool.all.GetLocal(hash) != nil } +func (pool *TxPool) RemoveTx(hash common.Hash) { + pool.mu.Lock() + defer pool.mu.Unlock() + + pool.removeTx(hash, true) +} + // removeTx removes a single transaction from the queue, moving all subsequent // transactions back to the future queue. func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { diff --git a/coreth/core/types/block.go b/coreth/core/types/block.go index ccf5a041..38cfd322 100644 --- a/coreth/core/types/block.go +++ b/coreth/core/types/block.go @@ -110,6 +110,9 @@ type Header struct { // BlockGasCost was added by Apricot Phase 4 and is ignored in legacy // headers. BlockGasCost *big.Int `json:"blockGasCost" rlp:"optional"` + + // ExtraStateRoot root was added by Cortina and is ignored in legacy headers. + ExtraStateRoot common.Hash `json:"extraStateRoot" rlp:"optional"` } // field type overrides for gencodec @@ -375,7 +378,7 @@ func (b *Block) Header() *Header { return CopyHeader(b.header) } func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles, b.version, b.extdata} } // Size returns the true RLP encoded storage size of the block, either by encoding -// and returning it, or returning a previsouly cached value. +// and returning it, or returning a previously cached value. func (b *Block) Size() common.StorageSize { if size := b.size.Load(); size != nil { return size.(common.StorageSize) diff --git a/coreth/core/types/bloom9.go b/coreth/core/types/bloom9.go index 872fa85f..aa172a0b 100644 --- a/coreth/core/types/bloom9.go +++ b/coreth/core/types/bloom9.go @@ -164,7 +164,7 @@ func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byt return i1, v1, i2, v2, i3, v3 } -// BloomLookup is a convenience-method to check presence int he bloom filter +// BloomLookup is a convenience-method to check presence in the bloom filter func BloomLookup(bin Bloom, topic bytesBacked) bool { return bin.Test(topic.Bytes()) } diff --git a/coreth/core/types/gen_header_json.go b/coreth/core/types/gen_header_json.go index 26f934c5..7036c7b5 100644 --- a/coreth/core/types/gen_header_json.go +++ b/coreth/core/types/gen_header_json.go @@ -35,6 +35,7 @@ func (h Header) MarshalJSON() ([]byte, error) { BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` ExtDataGasUsed *hexutil.Big `json:"extDataGasUsed" rlp:"optional"` BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"` + ExtraStateRoot common.Hash `json:"extraStateRoot" rlp:"optional"` Hash common.Hash `json:"hash"` } var enc Header @@ -57,6 +58,7 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.BaseFee = (*hexutil.Big)(h.BaseFee) enc.ExtDataGasUsed = (*hexutil.Big)(h.ExtDataGasUsed) enc.BlockGasCost = (*hexutil.Big)(h.BlockGasCost) + enc.ExtraStateRoot = h.ExtraStateRoot enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -83,6 +85,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` ExtDataGasUsed *hexutil.Big `json:"extDataGasUsed" rlp:"optional"` BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"` + ExtraStateRoot *common.Hash `json:"extraStateRoot" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -159,5 +162,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.BlockGasCost != nil { h.BlockGasCost = (*big.Int)(dec.BlockGasCost) } + if dec.ExtraStateRoot != nil { + h.ExtraStateRoot = *dec.ExtraStateRoot + } return nil } diff --git a/coreth/core/types/gen_header_rlp.go b/coreth/core/types/gen_header_rlp.go index 4838faae..84e20cb7 100644 --- a/coreth/core/types/gen_header_rlp.go +++ b/coreth/core/types/gen_header_rlp.go @@ -5,11 +5,9 @@ package types -import ( - "io" - - "github.com/ethereum/go-ethereum/rlp" -) +import "github.com/ethereum/go-ethereum/common" +import "github.com/ethereum/go-ethereum/rlp" +import "io" func (obj *Header) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) @@ -47,7 +45,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { _tmp1 := obj.BaseFee != nil _tmp2 := obj.ExtDataGasUsed != nil _tmp3 := obj.BlockGasCost != nil - if _tmp1 || _tmp2 || _tmp3 { + _tmp4 := obj.ExtraStateRoot != (common.Hash{}) + if _tmp1 || _tmp2 || _tmp3 || _tmp4 { if obj.BaseFee == nil { w.Write(rlp.EmptyString) } else { @@ -57,7 +56,7 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.WriteBigInt(obj.BaseFee) } } - if _tmp2 || _tmp3 { + if _tmp2 || _tmp3 || _tmp4 { if obj.ExtDataGasUsed == nil { w.Write(rlp.EmptyString) } else { @@ -67,7 +66,7 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.WriteBigInt(obj.ExtDataGasUsed) } } - if _tmp3 { + if _tmp3 || _tmp4 { if obj.BlockGasCost == nil { w.Write(rlp.EmptyString) } else { @@ -77,6 +76,9 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.WriteBigInt(obj.BlockGasCost) } } + if _tmp4 { + w.WriteBytes(obj.ExtraStateRoot[:]) + } w.ListEnd(_tmp0) return w.Flush() } diff --git a/coreth/core/vm/contracts.go b/coreth/core/vm/contracts.go index 9d53df76..4b959384 100644 --- a/coreth/core/vm/contracts.go +++ b/coreth/core/vm/contracts.go @@ -107,7 +107,25 @@ var PrecompiledContractsApricotPhase2 = map[common.Address]precompile.StatefulPr NativeAssetCallAddr: &nativeAssetCall{gasCost: params.AssetCallApricot}, } +// PrecompiledContractsBanff contains the default set of pre-compiled Ethereum +// contracts used in the Banff release. +var PrecompiledContractsBanff = map[common.Address]precompile.StatefulPrecompiledContract{ + common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}), + common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}), + common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}), + common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}), + common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{eip2565: true}), + common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddIstanbul{}), + common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}), + common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}), + common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}), + genesisContractAddr: &deprecatedContract{}, + NativeAssetBalanceAddr: &deprecatedContract{}, + NativeAssetCallAddr: &deprecatedContract{}, +} + var ( + PrecompiledAddressesBanff []common.Address PrecompiledAddressesApricotPhase2 []common.Address PrecompiledAddressesIstanbul []common.Address PrecompiledAddressesByzantium []common.Address @@ -128,12 +146,17 @@ func init() { for k := range PrecompiledContractsApricotPhase2 { PrecompiledAddressesApricotPhase2 = append(PrecompiledAddressesApricotPhase2, k) } + for k := range PrecompiledContractsBanff { + PrecompiledAddressesBanff = append(PrecompiledAddressesBanff, k) + } + // Set of all native precompile addresses that are in use // Note: this will repeat some addresses, but this is cheap and makes the code clearer. PrecompileAllNativeAddresses = make(map[common.Address]struct{}) addrsList := append(PrecompiledAddressesHomestead, PrecompiledAddressesByzantium...) addrsList = append(addrsList, PrecompiledAddressesIstanbul...) addrsList = append(addrsList, PrecompiledAddressesApricotPhase2...) + addrsList = append(addrsList, PrecompiledAddressesBanff...) for _, k := range addrsList { PrecompileAllNativeAddresses[k] = struct{}{} } @@ -165,6 +188,8 @@ func init() { // ActivePrecompiles returns the precompiles enabled with the current configuration. func ActivePrecompiles(rules params.Rules) []common.Address { switch { + case rules.IsBanff: + return PrecompiledAddressesBanff case rules.IsApricotPhase2: return PrecompiledAddressesApricotPhase2 case rules.IsIstanbul: diff --git a/coreth/core/vm/contracts_stateful_test.go b/coreth/core/vm/contracts_stateful_test.go index f89a7528..589fdc4f 100644 --- a/coreth/core/vm/contracts_stateful_test.go +++ b/coreth/core/vm/contracts_stateful_test.go @@ -468,7 +468,7 @@ func TestStatefulPrecompile(t *testing.T) { t.Run(test.name, func(t *testing.T) { stateDB := test.setupStateDB() // Create EVM with BlockNumber and Time initialized to 0 to enable Apricot Rules. - evm := NewEVM(vmCtx, TxContext{}, stateDB, params.TestChainConfig, Config{}) + evm := NewEVM(vmCtx, TxContext{}, stateDB, params.TestApricotPhase5Config, Config{}) // Use ApricotPhase5Config because these precompiles are deprecated in ApricotPhase6. ret, gasRemaining, err := evm.Call(AccountRef(test.from), test.precompileAddr, test.input, test.gasInput, test.value) // Place gas remaining check before error check, so that it is not skipped when there is an error assert.Equal(t, test.expectedGasRemaining, gasRemaining, "unexpected gas remaining") diff --git a/coreth/core/vm/evm.go b/coreth/core/vm/evm.go index d1bcec1d..8ece50f1 100644 --- a/coreth/core/vm/evm.go +++ b/coreth/core/vm/evm.go @@ -59,6 +59,27 @@ func IsProhibited(addr common.Address) bool { return false } +// TODO: deprecate after Banff activation. +func (evm *EVM) isProhibitedWithTimestamp(addr common.Address) error { + if addr != NativeAssetCallAddr { + return nil + } + + // Return error depending on the phase + switch { + case evm.chainRules.IsBanff: // Disable the soft fork as of Banff + return nil + case evm.chainRules.IsApricotPhasePost6: // If we are in the soft fork, return the soft error + return vmerrs.ErrToAddrProhibitedSoft + case evm.chainRules.IsApricotPhase6: // If we are in Phase6, return nil + return nil + case evm.chainRules.IsApricotPhasePre6: // If we are in PrePhase6, return Prohibited6 + return vmerrs.ErrToAddrProhibited6 + default: // Prior to Pre6, don't alter behavior at all + return nil + } +} + // emptyCodeHash is used by create to ensure deployment is disallowed to already // deployed contract addresses (relevant after the account abstraction). var emptyCodeHash = crypto.Keccak256Hash(nil) @@ -78,6 +99,8 @@ type ( func (evm *EVM) precompile(addr common.Address) (precompile.StatefulPrecompiledContract, bool) { var precompiles map[common.Address]precompile.StatefulPrecompiledContract switch { + case evm.chainRules.IsBanff: + precompiles = PrecompiledContractsBanff case evm.chainRules.IsApricotPhase2: precompiles = PrecompiledContractsApricotPhase2 case evm.chainRules.IsIstanbul: @@ -334,6 +357,9 @@ func (evm *EVM) CallWithoutSnapshot(caller ContractRef, addr common.Address, inp // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + if prohibitErr := evm.isProhibitedWithTimestamp(addr); prohibitErr != nil { + return nil, gas, prohibitErr + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, vmerrs.ErrDepth @@ -417,6 +443,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // This allows the user transfer balance of a specified coinId in addition to a normal Call(). func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, coinID common.Hash, value2 *big.Int) (ret []byte, leftOverGas uint64, err error) { + if prohibitErr := evm.isProhibitedWithTimestamp(addr); prohibitErr != nil { + return nil, gas, prohibitErr + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, vmerrs.ErrDepth @@ -500,6 +529,9 @@ func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte // CallCode differs from Call in the sense that it executes the given address' // code with the caller as context. func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + if prohibitErr := evm.isProhibitedWithTimestamp(addr); prohibitErr != nil { + return nil, gas, prohibitErr + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, vmerrs.ErrDepth @@ -551,6 +583,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // DelegateCall differs from CallCode in the sense that it executes the given address' // code with the caller as context and the caller is set to the caller of the caller. func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { + if prohibitErr := evm.isProhibitedWithTimestamp(addr); prohibitErr != nil { + return nil, gas, prohibitErr + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, vmerrs.ErrDepth @@ -590,6 +625,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Opcodes that attempt to perform such modifications will result in exceptions // instead of performing the modifications. func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { + if prohibitErr := evm.isProhibitedWithTimestamp(addr); prohibitErr != nil { + return nil, gas, prohibitErr + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, vmerrs.ErrDepth diff --git a/coreth/core/vm/instructions.go b/coreth/core/vm/instructions.go index 5344460c..960df8bc 100644 --- a/coreth/core/vm/instructions.go +++ b/coreth/core/vm/instructions.go @@ -618,6 +618,10 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal) + // Special case the error in the op code. TODO remove. + if errors.Is(suberr, vmerrs.ErrToAddrProhibitedSoft) { + return nil, suberr + } // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -664,6 +668,10 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, bigEndowment, &salt) + // Special case the error in the op code. TODO remove. + if errors.Is(suberr, vmerrs.ErrToAddrProhibitedSoft) { + return nil, suberr + } // Push item on the stack based on the returned error. if suberr != nil { stackvalue.Clear() @@ -706,7 +714,10 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) - + // Special case the error in the op code. TODO remove. + if errors.Is(err, vmerrs.ErrToAddrProhibitedSoft) { + return nil, err + } if err != nil { temp.Clear() } else { @@ -757,7 +768,10 @@ func opCallExpert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } ret, returnGas, err := interpreter.evm.CallExpert(scope.Contract, toAddr, args, gas, bigVal, coinID, bigVal2) - + // Special case the error in the op code. TODO remove. + if errors.Is(err, vmerrs.ErrToAddrProhibitedSoft) { + return nil, err + } if err != nil { temp.Clear() } else { @@ -793,6 +807,10 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal) + // Special case the error in the op code. TODO remove. + if errors.Is(err, vmerrs.ErrToAddrProhibitedSoft) { + return nil, err + } if err != nil { temp.Clear() } else { @@ -822,6 +840,10 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas) + // Special case the error in the op code. TODO remove. + if errors.Is(err, vmerrs.ErrToAddrProhibitedSoft) { + return nil, err + } if err != nil { temp.Clear() } else { @@ -851,6 +873,10 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas) + // Special case the error in the op code. TODO remove. + if errors.Is(err, vmerrs.ErrToAddrProhibitedSoft) { + return nil, err + } if err != nil { temp.Clear() } else { diff --git a/coreth/core/vm/runtime/runtime_test.go b/coreth/core/vm/runtime/runtime_test.go index 91985d4f..1b2106a2 100644 --- a/coreth/core/vm/runtime/runtime_test.go +++ b/coreth/core/vm/runtime/runtime_test.go @@ -343,7 +343,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) cfg.GasLimit = gas if len(tracerCode) > 0 { - tracer, err := tracers.New(tracerCode, new(tracers.Context)) + tracer, err := tracers.New(tracerCode, new(tracers.Context), nil) if err != nil { b.Fatal(err) } @@ -467,7 +467,7 @@ func BenchmarkSimpleLoop(b *testing.B) { byte(vm.JUMP), } - calllRevertingContractWithInput := []byte{ + callRevertingContractWithInput := []byte{ byte(vm.JUMPDEST), // // push args for the call byte(vm.PUSH1), 0, // out size @@ -495,7 +495,7 @@ func BenchmarkSimpleLoop(b *testing.B) { benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", "", b) benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", "", b) benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", "", b) - benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", "", b) + benchmarkNonModifyingCode(100000000, callRevertingContractWithInput, "call-reverting-100M", "", b) //benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b) //benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b) diff --git a/coreth/eth/api.go b/coreth/eth/api.go index 352c2562..5152ef20 100644 --- a/coreth/eth/api.go +++ b/coreth/eth/api.go @@ -92,7 +92,7 @@ func (api *AdminAPI) ExportChain(file string, first *uint64, last *uint64) (bool } if _, err := os.Stat(file); err == nil { // File already exists. Allowing overwrite could be a DoS vector, - // since the 'file' may point to arbitrary paths on the drive + // since the 'file' may point to arbitrary paths on the drive. return false, errors.New("location would overwrite an existing file") } // Make sure we can create the file to export into @@ -219,41 +219,11 @@ func (api *DebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.By return nil, errors.New("unknown preimage") } -// BadBlockArgs represents the entries in the list returned when bad blocks are queried. -type BadBlockArgs struct { - Hash common.Hash `json:"hash"` - Block map[string]interface{} `json:"block"` - RLP string `json:"rlp"` -} - // GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network // and returns them as a JSON list of block hashes. -func (api *DebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) { - var ( - err error - blocks = api.eth.BlockChain().BadBlocks() - results = make([]*BadBlockArgs, 0, len(blocks)) - ) - for _, block := range blocks { - var ( - blockRlp string - blockJSON map[string]interface{} - ) - if rlpBytes, err := rlp.EncodeToBytes(block); err != nil { - blockRlp = err.Error() // Hacky, but hey, it works - } else { - blockRlp = fmt.Sprintf("%#x", rlpBytes) - } - if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig()); err != nil { - blockJSON = map[string]interface{}{"error": err.Error()} - } - results = append(results, &BadBlockArgs{ - Hash: block.Hash(), - RLP: blockRlp, - Block: blockJSON, - }) - } - return results, nil +func (api *DebugAPI) GetBadBlocks(ctx context.Context) ([]*ethapi.BadBlockArgs, error) { + internalAPI := ethapi.NewBlockChainAPI(api.eth.APIBackend) + return internalAPI.GetBadBlocks(ctx) } // AccountRangeMaxResults is the maximum number of results to be returned per call @@ -419,11 +389,11 @@ func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]c } triedb := api.eth.BlockChain().StateCache().TrieDB() - oldTrie, err := trie.NewSecure(common.Hash{}, startBlock.Root(), triedb) + oldTrie, err := trie.NewStateTrie(common.Hash{}, startBlock.Root(), triedb) if err != nil { return nil, err } - newTrie, err := trie.NewSecure(common.Hash{}, endBlock.Root(), triedb) + newTrie, err := trie.NewStateTrie(common.Hash{}, endBlock.Root(), triedb) if err != nil { return nil, err } diff --git a/coreth/eth/api_backend.go b/coreth/eth/api_backend.go index c53213f5..6fb13ec0 100644 --- a/coreth/eth/api_backend.go +++ b/coreth/eth/api_backend.go @@ -29,7 +29,6 @@ package eth import ( "context" "errors" - "fmt" "math/big" "time" @@ -201,7 +200,7 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r return nil, errors.New("invalid arguments; neither block nor hash specified") } -func (b *EthAPIBackend) BadBlocks() []*types.Block { +func (b *EthAPIBackend) BadBlocks() ([]*types.Block, []*core.BadBlockReason) { return b.eth.blockchain.BadBlocks() } @@ -246,23 +245,8 @@ func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (type return b.eth.blockchain.GetReceiptsByHash(hash), nil } -func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) { - if err := ctx.Err(); err != nil { - return nil, err - } - - header, err := b.HeaderByHash(ctx, hash) - if header == nil || err != nil { - return nil, fmt.Errorf("failed to get block number for hash %#x", hash) - } - - db := b.eth.ChainDb() - number := header.Number.Uint64() - logs := rawdb.ReadLogs(db, hash, number) - if logs == nil { - return nil, fmt.Errorf("failed to get logs for block #%d (0x%s)", number, hash.TerminalString()) - } - return logs, nil +func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { + return rawdb.ReadLogs(b.eth.chainDb, hash, number), nil } func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) { diff --git a/coreth/eth/backend.go b/coreth/eth/backend.go index adaca919..7dd9b1a6 100644 --- a/coreth/eth/backend.go +++ b/coreth/eth/backend.go @@ -150,7 +150,7 @@ func New( "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024, ) - chainConfig, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + chainConfig, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis, lastAcceptedHash) if genesisErr != nil { return nil, genesisErr } @@ -283,6 +283,13 @@ func (s *Ethereum) APIs() []rpc.API { // Add the APIs from the node apis = append(apis, s.stackRPCs...) + // Create [filterSystem] with the log cache size set in the config. + ethcfg := s.APIBackend.eth.config + filterSystem := filters.NewFilterSystem(s.APIBackend, filters.Config{ + LogCacheSize: ethcfg.FilterLogCacheSize, + Timeout: 5 * time.Minute, + }) + // Append all the local APIs and return return append(apis, []rpc.API{ { @@ -291,7 +298,7 @@ func (s *Ethereum) APIs() []rpc.API { Name: "eth", }, { Namespace: "eth", - Service: filters.NewFilterAPI(s.APIBackend, false, 5*time.Minute), + Service: filters.NewFilterAPI(filterSystem, false /* isLightClient */), Name: "eth-filter", }, { Namespace: "admin", diff --git a/coreth/eth/ethconfig/config.go b/coreth/eth/ethconfig/config.go index 63cf965a..bb9c2671 100644 --- a/coreth/eth/ethconfig/config.go +++ b/coreth/eth/ethconfig/config.go @@ -70,6 +70,7 @@ func NewDefaultConfig() Config { TrieDirtyCache: 256, TrieDirtyCommitTarget: 20, SnapshotCache: 128, + FilterLogCacheSize: 32, Miner: miner.Config{}, TxPool: core.DefaultTxPoolConfig, RPCGasCap: 25000000, @@ -148,6 +149,9 @@ type Config struct { SnapshotCache int Preimages bool + // This is the number of blocks for which logs will be cached in the filter system. + FilterLogCacheSize int + // Mining options Miner miner.Config @@ -170,7 +174,7 @@ type Config struct { RPCEVMTimeout time.Duration // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for - // send-transction variants. The unit is ether. + // send-transaction variants. The unit is ether. RPCTxFeeCap float64 `toml:",omitempty"` // AllowUnfinalizedQueries allow unfinalized queries diff --git a/coreth/eth/filters/api.go b/coreth/eth/filters/api.go index eba32cdb..1c30597d 100644 --- a/coreth/eth/filters/api.go +++ b/coreth/eth/filters/api.go @@ -47,7 +47,7 @@ import ( // and associated subscription in the event system. type filter struct { typ Type - deadline *time.Timer // filter is inactiv when deadline triggers + deadline *time.Timer // filter is inactive when deadline triggers hashes []common.Hash crit FilterCriteria logs []*types.Log @@ -57,22 +57,22 @@ type filter struct { // FilterAPI offers support to create and manage filters. This will allow external clients to retrieve various // information related to the Ethereum protocol such als blocks, transactions and logs. type FilterAPI struct { - backend Backend + sys *FilterSystem events *EventSystem filtersMu sync.Mutex filters map[rpc.ID]*filter timeout time.Duration } -// NewFilterAPI returns a new PublicFilterAPI instance. -func NewFilterAPI(backend Backend, lightMode bool, timeout time.Duration) *FilterAPI { +// NewFilterAPI returns a new FilterAPI instance. +func NewFilterAPI(system *FilterSystem, lightMode bool) *FilterAPI { api := &FilterAPI{ - backend: backend, - events: NewEventSystem(backend, lightMode), + sys: system, + events: NewEventSystem(system, lightMode), filters: make(map[rpc.ID]*filter), - timeout: timeout, + timeout: system.cfg.Timeout, } - go api.timeoutLoop(timeout) + go api.timeoutLoop(system.cfg.Timeout) return api } @@ -218,7 +218,7 @@ func (api *FilterAPI) NewBlockFilter() rpc.ID { headerSub *Subscription ) - if api.backend.GetVMConfig().AllowUnfinalizedQueries { + if api.sys.backend.GetVMConfig().AllowUnfinalizedQueries { headerSub = api.events.SubscribeNewHeads(headers) } else { headerSub = api.events.SubscribeAcceptedHeads(headers) @@ -264,7 +264,7 @@ func (api *FilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { headersSub event.Subscription ) - if api.backend.GetVMConfig().AllowUnfinalizedQueries { + if api.sys.backend.GetVMConfig().AllowUnfinalizedQueries { headersSub = api.events.SubscribeNewHeads(headers) } else { headersSub = api.events.SubscribeAcceptedHeads(headers) @@ -301,7 +301,7 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc err error ) - if api.backend.GetVMConfig().AllowUnfinalizedQueries { + if api.sys.backend.GetVMConfig().AllowUnfinalizedQueries { logsSub, err = api.events.SubscribeLogs(interfaces.FilterQuery(crit), matchedLogs) if err != nil { return nil, err @@ -356,7 +356,7 @@ func (api *FilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { err error ) - if api.backend.GetVMConfig().AllowUnfinalizedQueries { + if api.sys.backend.GetVMConfig().AllowUnfinalizedQueries { logsSub, err = api.events.SubscribeLogs(interfaces.FilterQuery(crit), logs) if err != nil { return rpc.ID(""), err @@ -398,7 +398,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type var filter *Filter if crit.BlockHash != nil { // Block filter requested, construct a single-shot filter - filter = NewBlockFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics) + filter = api.sys.NewBlockFilter(*crit.BlockHash, crit.Addresses, crit.Topics) } else { // Convert the RPC block numbers into internal representations // LatestBlockNumber is left in place here to be handled @@ -413,7 +413,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type } // Construct the range filter var err error - filter, err = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics) + filter, err = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) if err != nil { return nil, err } @@ -455,7 +455,7 @@ func (api *FilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Lo var filter *Filter if f.crit.BlockHash != nil { // Block filter requested, construct a single-shot filter - filter = NewBlockFilter(api.backend, *f.crit.BlockHash, f.crit.Addresses, f.crit.Topics) + filter = api.sys.NewBlockFilter(*f.crit.BlockHash, f.crit.Addresses, f.crit.Topics) } else { // Convert the RPC block numbers into internal representations // Leave LatestBlockNumber in place here as the defaults @@ -471,7 +471,7 @@ func (api *FilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Lo } // Construct the range filter var err error - filter, err = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics) + filter, err = api.sys.NewRangeFilter(begin, end, f.crit.Addresses, f.crit.Topics) if err != nil { return nil, err } diff --git a/coreth/eth/filters/filter.go b/coreth/eth/filters/filter.go index 88614048..02b0d57e 100644 --- a/coreth/eth/filters/filter.go +++ b/coreth/eth/filters/filter.go @@ -32,49 +32,16 @@ import ( "fmt" "math/big" - "github.com/ava-labs/coreth/core/vm" - - "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/bloombits" "github.com/ava-labs/coreth/core/types" - "github.com/ava-labs/coreth/ethdb" "github.com/ava-labs/coreth/rpc" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/event" ) -type Backend interface { - ChainDb() ethdb.Database - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) - HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) - - SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription - SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription - SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription - SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription - SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription - SubscribeAcceptedLogsEvent(ch chan<- []*types.Log) event.Subscription - - SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription - - SubscribeAcceptedTransactionEvent(ch chan<- core.NewTxsEvent) event.Subscription - - BloomStatus() (uint64, uint64) - ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) - - // Added to the backend interface to support limiting of logs requests - GetVMConfig() *vm.Config - LastAcceptedBlock() *types.Block - GetMaxBlocksPerRequest() int64 -} - // Filter can be used to retrieve and filter logs. type Filter struct { - backend Backend + sys *FilterSystem - db ethdb.Database addresses []common.Address topics [][]common.Hash @@ -86,9 +53,9 @@ type Filter struct { // NewRangeFilter creates a new filter which uses a bloom filter on blocks to // figure out whether a particular block is interesting or not. -func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) (*Filter, error) { - allowUnfinalizedQueries := backend.GetVMConfig().AllowUnfinalizedQueries - acceptedBlock := backend.LastAcceptedBlock() +func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Address, topics [][]common.Hash) (*Filter, error) { + allowUnfinalizedQueries := sys.backend.GetVMConfig().AllowUnfinalizedQueries + acceptedBlock := sys.backend.LastAcceptedBlock() // Flatten the address and topic filter clauses into a single bloombits filter // system. Since the bloombits are not positional, nil topics are permitted, @@ -108,7 +75,7 @@ func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Addres } filters = append(filters, filter) } - size, _ := backend.BloomStatus() + size, _ := sys.backend.BloomStatus() if !allowUnfinalizedQueries && acceptedBlock != nil { lastAccepted := acceptedBlock.Number().Int64() @@ -121,7 +88,7 @@ func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Addres } // Create a generic filter and convert it into a range filter - filter := newFilter(backend, addresses, topics) + filter := newFilter(sys, addresses, topics) filter.matcher = bloombits.NewMatcher(size, filters) filter.begin = begin @@ -132,21 +99,20 @@ func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Addres // NewBlockFilter creates a new filter which directly inspects the contents of // a block to figure out whether it is interesting or not. -func NewBlockFilter(backend Backend, block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter { +func (sys *FilterSystem) NewBlockFilter(block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter { // Create a generic filter and convert it into a block filter - filter := newFilter(backend, addresses, topics) + filter := newFilter(sys, addresses, topics) filter.block = block return filter } // newFilter creates a generic filter that can either filter based on a block hash, // or based on range queries. The search criteria needs to be explicitly set. -func newFilter(backend Backend, addresses []common.Address, topics [][]common.Hash) *Filter { +func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter { return &Filter{ - backend: backend, + sys: sys, addresses: addresses, topics: topics, - db: backend.ChainDb(), } } @@ -155,14 +121,14 @@ func newFilter(backend Backend, addresses []common.Address, topics [][]common.Ha func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { // If we're doing singleton block filtering, execute and return if f.block != (common.Hash{}) { - header, err := f.backend.HeaderByHash(ctx, f.block) + header, err := f.sys.backend.HeaderByHash(ctx, f.block) if err != nil { return nil, err } if header == nil { return nil, errors.New("unknown block") } - return f.blockLogs(ctx, header) + return f.blockLogs(ctx, header, false) } // Short-cut if all we care about is pending logs if f.begin == rpc.PendingBlockNumber.Int64() { @@ -175,7 +141,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { // Figure out the limits of the filter range // LatestBlockNumber is transformed into the last accepted block in HeaderByNumber // so it is left in place here. - header, err := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + header, err := f.sys.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) if err != nil { return nil, err } @@ -205,13 +171,13 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { // If the requested range of blocks exceeds the maximum number of blocks allowed by the backend // return an error instead of searching for the logs. - if maxBlocks := f.backend.GetMaxBlocksPerRequest(); int64(end)-f.begin > maxBlocks && maxBlocks > 0 { + if maxBlocks := f.sys.backend.GetMaxBlocksPerRequest(); int64(end)-f.begin > maxBlocks && maxBlocks > 0 { return nil, fmt.Errorf("requested too many blocks from %d to %d, maximum is set to %d", f.begin, int64(end), maxBlocks) } // Gather all indexed logs, and finish with non indexed ones var ( logs []*types.Log - size, sections = f.backend.BloomStatus() + size, sections = f.sys.backend.BloomStatus() ) if indexed := sections * size; indexed > uint64(f.begin) { if indexed > end { @@ -240,7 +206,7 @@ func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, err } defer session.Close() - f.backend.ServiceFilter(ctx, session) + f.sys.backend.ServiceFilter(ctx, session) // Iterate over the matches until exhausted or context closed var logs []*types.Log @@ -259,11 +225,11 @@ func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, err f.begin = int64(number) + 1 // Retrieve the suggested block and pull any truly matching logs - header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) + header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) if header == nil || err != nil { return logs, err } - found, err := f.checkMatches(ctx, header) + found, err := f.blockLogs(ctx, header, true) if err != nil { return logs, err } @@ -281,11 +247,11 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e var logs []*types.Log for ; f.begin <= int64(end); f.begin++ { - header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) + header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) if header == nil || err != nil { return logs, err } - found, err := f.blockLogs(ctx, header) + found, err := f.blockLogs(ctx, header, false) if err != nil { return logs, err } @@ -295,34 +261,34 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e } // blockLogs returns the logs matching the filter criteria within a single block. -func (f *Filter) blockLogs(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { - if bloomFilter(header.Bloom, f.addresses, f.topics) { - found, err := f.checkMatches(ctx, header) +func (f *Filter) blockLogs(ctx context.Context, header *types.Header, skipBloom bool) ([]*types.Log, error) { + // Fast track: no filtering criteria + if len(f.addresses) == 0 && len(f.topics) == 0 { + list, err := f.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64()) if err != nil { - return logs, err + return nil, err } - logs = append(logs, found...) + return flatten(list), nil + } else if skipBloom || bloomFilter(header.Bloom, f.addresses, f.topics) { + return f.checkMatches(ctx, header) } - return logs, nil + return nil, nil } // checkMatches checks if the receipts belonging to the given header contain any log events that // match the filter criteria. This function is called when the bloom filter signals a potential match. -func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { - // Get the logs of the block - logsList, err := f.backend.GetLogs(ctx, header.Hash()) +func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) { + logsList, err := f.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64()) if err != nil { return nil, err } - var unfiltered []*types.Log - for _, logs := range logsList { - unfiltered = append(unfiltered, logs...) - } - logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) + + unfiltered := flatten(logsList) + logs := filterLogs(unfiltered, nil, nil, f.addresses, f.topics) if len(logs) > 0 { // We have matching logs, check if we need to resolve full logs via the light client if logs[0].TxHash == (common.Hash{}) { - receipts, err := f.backend.GetReceipts(ctx, header.Hash()) + receipts, err := f.sys.backend.GetReceipts(ctx, header.Hash()) if err != nil { return nil, err } @@ -411,3 +377,11 @@ func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]commo } return true } + +func flatten(list [][]*types.Log) []*types.Log { + var flat []*types.Log + for _, logs := range list { + flat = append(flat, logs...) + } + return flat +} diff --git a/coreth/eth/filters/filter_system.go b/coreth/eth/filters/filter_system.go index 81e353e9..50191198 100644 --- a/coreth/eth/filters/filter_system.go +++ b/coreth/eth/filters/filter_system.go @@ -35,15 +35,102 @@ import ( "time" "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/bloombits" "github.com/ava-labs/coreth/core/rawdb" "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/core/vm" + "github.com/ava-labs/coreth/ethdb" "github.com/ava-labs/coreth/interfaces" "github.com/ava-labs/coreth/rpc" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + lru "github.com/hashicorp/golang-lru" ) +// Config represents the configuration of the filter system. +type Config struct { + LogCacheSize int // maximum number of cached blocks (default: 32) + Timeout time.Duration // how long filters stay active (default: 5min) +} + +func (cfg Config) withDefaults() Config { + if cfg.Timeout == 0 { + cfg.Timeout = 5 * time.Minute + } + if cfg.LogCacheSize == 0 { + cfg.LogCacheSize = 32 + } + return cfg +} + +type Backend interface { + ChainDb() ethdb.Database + HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) + HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) + GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) + GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) + + SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription + SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription + SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription + SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription + SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription + SubscribeAcceptedLogsEvent(ch chan<- []*types.Log) event.Subscription + + SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription + + SubscribeAcceptedTransactionEvent(ch chan<- core.NewTxsEvent) event.Subscription + + BloomStatus() (uint64, uint64) + ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) + + // Added to the backend interface to support limiting of logs requests + GetVMConfig() *vm.Config + LastAcceptedBlock() *types.Block + GetMaxBlocksPerRequest() int64 +} + +// FilterSystem holds resources shared by all filters. +type FilterSystem struct { + backend Backend + logsCache *lru.Cache + cfg *Config +} + +// NewFilterSystem creates a filter system. +func NewFilterSystem(backend Backend, config Config) *FilterSystem { + config = config.withDefaults() + + cache, err := lru.New(config.LogCacheSize) + if err != nil { + panic(err) + } + return &FilterSystem{ + backend: backend, + logsCache: cache, + cfg: &config, + } +} + +// cachedGetLogs loads block logs from the backend and caches the result. +func (sys *FilterSystem) cachedGetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { + cached, ok := sys.logsCache.Get(blockHash) + if ok { + return cached.([][]*types.Log), nil + } + + logs, err := sys.backend.GetLogs(ctx, blockHash, number) + if err != nil { + return nil, err + } + if logs == nil { + return nil, fmt.Errorf("failed to get logs for block #%d (0x%s)", number, blockHash.TerminalString()) + } + sys.logsCache.Add(blockHash, logs) + return logs, nil +} + // Type determines the kind of filter and is used to put the filter in to // the correct bucket when added. type Type byte @@ -100,6 +187,7 @@ type subscription struct { // subscription which match the subscription criteria. type EventSystem struct { backend Backend + sys *FilterSystem lightMode bool lastHead *types.Header @@ -132,9 +220,10 @@ type EventSystem struct { // // The returned manager has a loop that needs to be stopped with the Stop function // or by stopping the given mux. -func NewEventSystem(backend Backend, lightMode bool) *EventSystem { +func NewEventSystem(sys *FilterSystem, lightMode bool) *EventSystem { m := &EventSystem{ - backend: backend, + sys: sys, + backend: sys.backend, lightMode: lightMode, install: make(chan *subscription), uninstall: make(chan *subscription), @@ -537,7 +626,7 @@ func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common. // Get the logs of the block ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - logsList, err := es.backend.GetLogs(ctx, header.Hash()) + logsList, err := es.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64()) if err != nil { return nil } diff --git a/coreth/eth/state_accessor.go b/coreth/eth/state_accessor.go index bc45f61b..3e5f4c99 100644 --- a/coreth/eth/state_accessor.go +++ b/coreth/eth/state_accessor.go @@ -55,7 +55,7 @@ import ( // perform Commit or other 'save-to-disk' changes, this should be set to false to avoid // storing trash persistently // - preferDisk: this arg can be used by the caller to signal that even though the 'base' is provided, -// it would be preferrable to start from a fresh state, if we have it on disk. +// it would be preferable to start from a fresh state, if we have it on disk. func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) { var ( current *types.Block diff --git a/coreth/eth/tracers/api.go b/coreth/eth/tracers/api.go index 1625675d..03fb5be8 100644 --- a/coreth/eth/tracers/api.go +++ b/coreth/eth/tracers/api.go @@ -29,6 +29,7 @@ package tracers import ( "bytes" "context" + "encoding/json" "errors" "fmt" "math/big" @@ -77,7 +78,7 @@ type Backend interface { HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) - BadBlocks() []*types.Block + BadBlocks() ([]*types.Block, []*core.BadBlockReason) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) RPCGasCap() uint64 ChainConfig() *params.ChainConfig @@ -121,7 +122,7 @@ func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.H return header } -// chainContext construts the context reader which is used by the evm for reading +// chainContext constructs the context reader which is used by the evm for reading // the necessary chain context. func (api *API) chainContext(ctx context.Context) core.ChainContext { return &chainContext{api: api, ctx: ctx} @@ -175,15 +176,15 @@ type TraceConfig struct { Tracer *string Timeout *string Reexec *uint64 + // Config specific to given tracer. Note struct logger + // config are historically embedded in main object. + TracerConfig json.RawMessage } // TraceCallConfig is the config for traceCall API. It holds one more // field to override the state for tracing. type TraceCallConfig struct { - *logger.Config - Tracer *string - Timeout *string - Reexec *uint64 + TraceConfig StateOverrides *ethapi.StateOverride BlockOverrides *ethapi.BlockOverrides } @@ -378,7 +379,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config } if trieDb := statedb.Database().TrieDB(); trieDb != nil { // Hold the reference for tracer, will be released at the final stage - trieDb.Reference(block.Root(), common.Hash{}, true) + trieDb.Reference(block.Root(), common.Hash{}) // Release the parent state because it's already held by the tracer if parent != (common.Hash{}) { @@ -476,8 +477,8 @@ func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *Trac func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { // Search for the bad block corresponding to [hash]. var ( - badBlocks = api.backend.BadBlocks() - block *types.Block + badBlocks, _ = api.backend.BadBlocks() + block *types.Block ) for _, badBlock := range badBlocks { if hash == block.Hash() { @@ -546,7 +547,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config // traceBlock configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item -// per transaction, dependent on the requestd tracer. +// per transaction, dependent on the requested tracer. func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { if block.NumberU64() == 0 { return nil, errors.New("genesis is not traceable") @@ -738,7 +739,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex // Default tracer is the struct logger tracer = logger.NewStructLogger(config.Config) if config.Tracer != nil { - tracer, err = New(*config.Tracer, txctx) + tracer, err = New(*config.Tracer, txctx, config.TracerConfig) if err != nil { return nil, err } diff --git a/coreth/eth/tracers/api_test.go b/coreth/eth/tracers/api_test.go index 96dceedb..18220495 100644 --- a/coreth/eth/tracers/api_test.go +++ b/coreth/eth/tracers/api_test.go @@ -132,7 +132,7 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) return b.chain.GetBlockByNumber(uint64(number)), nil } -func (b *testBackend) BadBlocks() []*types.Block { return nil } +func (b *testBackend) BadBlocks() ([]*types.Block, []*core.BadBlockReason) { return nil, nil } func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) diff --git a/coreth/eth/tracers/internal/tracetest/calltrace_test.go b/coreth/eth/tracers/internal/tracetest/calltrace_test.go index 79f1459b..340c35d0 100644 --- a/coreth/eth/tracers/internal/tracetest/calltrace_test.go +++ b/coreth/eth/tracers/internal/tracetest/calltrace_test.go @@ -127,10 +127,11 @@ type callTrace struct { // callTracerTest defines a single test to check the call tracer against. type callTracerTest struct { - Genesis *core.Genesis `json:"genesis"` - Context *callContext `json:"context"` - Input string `json:"input"` - Result *callTrace `json:"result"` + Genesis *core.Genesis `json:"genesis"` + Context *callContext `json:"context"` + Input string `json:"input"` + TracerConfig json.RawMessage `json:"tracerConfig"` + Result *callTrace `json:"result"` } func TestCallTracerNative(t *testing.T) { @@ -182,7 +183,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { } _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) ) - tracer, err := tracers.New(tracerName, new(tracers.Context)) + tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } @@ -297,7 +298,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - tracer, err := tracers.New(tracerName, new(tracers.Context)) + tracer, err := tracers.New(tracerName, new(tracers.Context), nil) if err != nil { b.Fatalf("failed to create call tracer: %v", err) } @@ -363,7 +364,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { } _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) // Create the tracer, the EVM environment and run it - tracer, err := tracers.New("callTracer", nil) + tracer, err := tracers.New("callTracer", nil, nil) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } diff --git a/coreth/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json b/coreth/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json new file mode 100644 index 00000000..ac1fef44 --- /dev/null +++ b/coreth/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json @@ -0,0 +1,72 @@ +{ + "context": { + "difficulty": "3502894804", + "gasLimit": "4722976", + "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724", + "number": "2289806", + "timestamp": "1513601314" + }, + "genesis": { + "alloc": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "code": "0x", + "nonce": "22", + "storage": {} + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "nonce": "1", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "code": "0x", + "nonce": "29072", + "storage": {} + } + }, + "config": { + "byzantiumBlock": 1700000, + "chainId": 3, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", + "eip155Block": 10, + "eip158Block": 10, + "ethash": {}, + "homesteadBlock": 0 + }, + "difficulty": "3509749784", + "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", + "gasLimit": "4727564", + "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440", + "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", + "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada", + "nonce": "0x4eb12e19c16d43da", + "number": "2289805", + "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f", + "timestamp": "1513601261", + "totalDifficulty": "7143276353481064" + }, + "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4", + "tracerConfig": { + "onlyTopCall": true + }, + "result": { + "from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb", + "gas": "0x10738", + "gasUsed": "0x3ef9", + "input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", + "type": "CALL", + "value": "0x0" + } +} diff --git a/coreth/eth/tracers/native/4byte.go b/coreth/eth/tracers/native/4byte.go index 4b4bd12a..1b7eced7 100644 --- a/coreth/eth/tracers/native/4byte.go +++ b/coreth/eth/tracers/native/4byte.go @@ -65,11 +65,11 @@ type fourByteTracer struct { // newFourByteTracer returns a native go tracer which collects // 4 byte-identifiers of a tx, and implements vm.EVMLogger. -func newFourByteTracer(ctx *tracers.Context) tracers.Tracer { +func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { t := &fourByteTracer{ ids: make(map[string]int), } - return t + return t, nil } // isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go diff --git a/coreth/eth/tracers/native/call.go b/coreth/eth/tracers/native/call.go index dc18a956..bc01277b 100644 --- a/coreth/eth/tracers/native/call.go +++ b/coreth/eth/tracers/native/call.go @@ -60,16 +60,27 @@ type callFrame struct { type callTracer struct { env *vm.EVM callstack []callFrame + config callTracerConfig interrupt uint32 // Atomic flag to signal execution interruption reason error // Textual reason for the interruption } +type callTracerConfig struct { + OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls +} + // newCallTracer returns a native go tracer which tracks // call frames of a tx, and implements vm.EVMLogger. -func newCallTracer(ctx *tracers.Context) tracers.Tracer { +func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { + var config callTracerConfig + if cfg != nil { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err + } + } // First callframe contains tx context info // and is populated on start and end. - return &callTracer{callstack: make([]callFrame, 1)} + return &callTracer{callstack: make([]callFrame, 1), config: config}, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. @@ -111,6 +122,9 @@ func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ * // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if t.config.OnlyTopCall { + return + } // Skip if tracing was interrupted if atomic.LoadUint32(&t.interrupt) > 0 { t.env.Cancel() @@ -131,6 +145,9 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common. // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { + if t.config.OnlyTopCall { + return + } size := len(t.callstack) if size <= 1 { return diff --git a/coreth/eth/tracers/native/noop.go b/coreth/eth/tracers/native/noop.go index 7fe4ab89..23884900 100644 --- a/coreth/eth/tracers/native/noop.go +++ b/coreth/eth/tracers/native/noop.go @@ -45,8 +45,8 @@ func init() { type noopTracer struct{} // newNoopTracer returns a new noop tracer. -func newNoopTracer(ctx *tracers.Context) tracers.Tracer { - return &noopTracer{} +func newNoopTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { + return &noopTracer{}, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. diff --git a/coreth/eth/tracers/native/prestate.go b/coreth/eth/tracers/native/prestate.go index afc51c9f..30a2bc74 100644 --- a/coreth/eth/tracers/native/prestate.go +++ b/coreth/eth/tracers/native/prestate.go @@ -61,10 +61,10 @@ type prestateTracer struct { reason error // Textual reason for the interruption } -func newPrestateTracer(ctx *tracers.Context) tracers.Tracer { +func newPrestateTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { // First callframe contains tx context info // and is populated on start and end. - return &prestateTracer{prestate: prestate{}} + return &prestateTracer{prestate: prestate{}}, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. diff --git a/coreth/eth/tracers/native/revertreason.go b/coreth/eth/tracers/native/revertreason.go index e1a0d78a..5056ae7c 100644 --- a/coreth/eth/tracers/native/revertreason.go +++ b/coreth/eth/tracers/native/revertreason.go @@ -57,8 +57,8 @@ type revertReasonTracer struct { } // newRevertReasonTracer returns a new revert reason tracer. -func newRevertReasonTracer(_ *tracers.Context) tracers.Tracer { - return &revertReasonTracer{} +func newRevertReasonTracer(_ *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { + return &revertReasonTracer{}, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. diff --git a/coreth/eth/tracers/native/tracer.go b/coreth/eth/tracers/native/tracer.go index 41ba6f35..102e6885 100644 --- a/coreth/eth/tracers/native/tracer.go +++ b/coreth/eth/tracers/native/tracer.go @@ -45,6 +45,7 @@ func init() { package native import ( + "encoding/json" "errors" "github.com/ava-labs/coreth/eth/tracers" @@ -56,7 +57,7 @@ func init() { } // ctorFn is the constructor signature of a native tracer. -type ctorFn = func(*tracers.Context) tracers.Tracer +type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error) /* ctors is a map of package-local tracer constructors. @@ -81,12 +82,12 @@ func register(name string, ctor ctorFn) { } // lookup returns a tracer, if one can be matched to the given name. -func lookup(name string, ctx *tracers.Context) (tracers.Tracer, error) { +func lookup(name string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { if ctors == nil { ctors = make(map[string]ctorFn) } if ctor, ok := ctors[name]; ok { - return ctor(ctx), nil + return ctor(ctx, cfg) } return nil, errors.New("no tracer found") } diff --git a/coreth/eth/tracers/tracers.go b/coreth/eth/tracers/tracers.go index cb1576f6..c8e1b5a2 100644 --- a/coreth/eth/tracers/tracers.go +++ b/coreth/eth/tracers/tracers.go @@ -42,7 +42,7 @@ type Tracer interface { Stop(err error) } -type lookupFunc func(string, *Context) (Tracer, error) +type lookupFunc func(string, *Context, json.RawMessage) (Tracer, error) var ( lookups []lookupFunc @@ -62,9 +62,9 @@ func RegisterLookup(wildcard bool, lookup lookupFunc) { // New returns a new instance of a tracer, by iterating through the // registered lookups. -func New(code string, ctx *Context) (Tracer, error) { +func New(code string, ctx *Context, cfg json.RawMessage) (Tracer, error) { for _, lookup := range lookups { - if tracer, err := lookup(code, ctx); err == nil { + if tracer, err := lookup(code, ctx, cfg); err == nil { return tracer, nil } } diff --git a/coreth/ethclient/ethclient.go b/coreth/ethclient/ethclient.go index 76ea4898..f74948ba 100644 --- a/coreth/ethclient/ethclient.go +++ b/coreth/ethclient/ethclient.go @@ -98,6 +98,7 @@ type Client interface { CallContractAtHash(ctx context.Context, msg interfaces.CallMsg, blockHash common.Hash) ([]byte, error) SuggestGasPrice(context.Context) (*big.Int, error) SuggestGasTipCap(context.Context) (*big.Int, error) + FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*interfaces.FeeHistory, error) EstimateGas(context.Context, interfaces.CallMsg) (uint64, error) EstimateBaseFee(context.Context) (*big.Int, error) SendTransaction(context.Context, *types.Transaction) error @@ -557,6 +558,38 @@ func (ec *client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return (*big.Int)(&hex), nil } +type feeHistoryResultMarshaling struct { + OldestBlock *hexutil.Big `json:"oldestBlock"` + Reward [][]*hexutil.Big `json:"reward,omitempty"` + BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` + GasUsedRatio []float64 `json:"gasUsedRatio"` +} + +// FeeHistory retrieves the fee market history. +func (ec *client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*interfaces.FeeHistory, error) { + var res feeHistoryResultMarshaling + if err := ec.c.CallContext(ctx, &res, "eth_feeHistory", hexutil.Uint(blockCount), ToBlockNumArg(lastBlock), rewardPercentiles); err != nil { + return nil, err + } + reward := make([][]*big.Int, len(res.Reward)) + for i, r := range res.Reward { + reward[i] = make([]*big.Int, len(r)) + for j, r := range r { + reward[i][j] = (*big.Int)(r) + } + } + baseFee := make([]*big.Int, len(res.BaseFee)) + for i, b := range res.BaseFee { + baseFee[i] = (*big.Int)(b) + } + return &interfaces.FeeHistory{ + OldestBlock: (*big.Int)(res.OldestBlock), + Reward: reward, + BaseFee: baseFee, + GasUsedRatio: res.GasUsedRatio, + }, nil +} + // EstimateGas tries to estimate the gas needed to execute a specific transaction based on // the current pending state of the backend blockchain. There is no guarantee that this is // the true gas limit requirement as other transactions may be added or removed by miners, diff --git a/coreth/ethdb/memorydb/memorydb.go b/coreth/ethdb/memorydb/memorydb.go index 6e2f4552..dbc9adac 100644 --- a/coreth/ethdb/memorydb/memorydb.go +++ b/coreth/ethdb/memorydb/memorydb.go @@ -72,7 +72,7 @@ func NewWithCap(size int) *Database { } // Close deallocates the internal map and ensures any consecutive data access op -// failes with an error. +// fails with an error. func (db *Database) Close() error { db.lock.Lock() defer db.lock.Unlock() diff --git a/coreth/go.mod b/coreth/go.mod index 2108cd8a..35533cef 100644 --- a/coreth/go.mod +++ b/coreth/go.mod @@ -4,11 +4,11 @@ go 1.18 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanchego v1.7.17 + github.com/ava-labs/avalanchego v1.8.5 github.com/cespare/cp v0.1.0 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v1.8.0 - github.com/ethereum/go-ethereum v1.10.21 + github.com/ethereum/go-ethereum v1.10.25 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 github.com/google/uuid v1.2.0 @@ -82,7 +82,6 @@ require ( github.com/jessevdk/go-flags v1.5.0 // indirect github.com/jrick/logrotate v1.0.0 // indirect github.com/kkdai/bstream v1.0.0 // indirect - github.com/linxGnu/grocksdb v1.6.34 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect @@ -117,7 +116,7 @@ require ( golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto v0.0.0-20220712132514-bdd2acd4974d // indirect - google.golang.org/grpc v1.47.0 // indirect + google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/coreth/go.sum b/coreth/go.sum index 8bac6a3c..25bb562a 100644 --- a/coreth/go.sum +++ b/coreth/go.sum @@ -60,8 +60,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/ava-labs/avalanchego v1.7.17 h1:+Kafk8GaveL/J97yiw+DDtt8TaLWDsyrB61p/QTiEuU= -github.com/ava-labs/avalanchego v1.7.17/go.mod h1:PDJudeWWz4IXxexK95XRMuziAv6XPpMPbCHGwwlzNfw= +github.com/ava-labs/avalanchego v1.8.5 h1:HhNJUsvwk+DxxncutN/4rlnUmnQvtF/73qNbpW3BFJc= +github.com/ava-labs/avalanchego v1.8.5/go.mod h1:GAo3tDCj5Ulx9xIY78ad5SmGFqtdmFRjJGQ2gJk//Vk= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -153,8 +153,8 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.21 h1:5lqsEx92ZaZzRyOqBEXux4/UR06m296RGzN3ol3teJY= -github.com/ethereum/go-ethereum v1.10.21/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +github.com/ethereum/go-ethereum v1.10.25 h1:5dFrKJDnYf8L6/5o42abCE6a9yJm9cs4EJVRyYMr55s= +github.com/ethereum/go-ethereum v1.10.25/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= @@ -366,8 +366,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/linxGnu/grocksdb v1.6.34 h1:fHNRbWepGN1zA4FFt/9LuiOPtSxDsr8mn4/RlU5+g1Q= -github.com/linxGnu/grocksdb v1.6.34/go.mod h1:/+iSQrn7Izt6kFhHBQvcE6FkklsKXa8hc35pFyFDrDw= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -942,8 +940,9 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/coreth/interfaces/interfaces.go b/coreth/interfaces/interfaces.go index 100e658c..f0b09c67 100644 --- a/coreth/interfaces/interfaces.go +++ b/coreth/interfaces/interfaces.go @@ -176,6 +176,15 @@ type GasPricer interface { SuggestGasPrice(ctx context.Context) (*big.Int, error) } +// FeeHistory provides recent fee market data that consumers can use to determine +// a reasonable maxPriorityFeePerGas value. +type FeeHistory struct { + OldestBlock *big.Int // block corresponding to first response value + Reward [][]*big.Int // list every txs priority fee per block + BaseFee []*big.Int // list of each block's base fee + GasUsedRatio []float64 // ratio of gas used out of the total available limit +} + // An AcceptedStateReceiver provides access to the accepted state ie. the state of the // most recently accepted block. type AcceptedStateReader interface { diff --git a/coreth/internal/ethapi/api.go b/coreth/internal/ethapi/api.go index 3a976caf..2be321b6 100644 --- a/coreth/internal/ethapi/api.go +++ b/coreth/internal/ethapi/api.go @@ -101,6 +101,7 @@ type feeHistoryResult struct { GasUsedRatio []float64 `json:"gasUsedRatio"` } +// FeeHistory returns the fee market history. func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles) if err != nil { @@ -137,6 +138,11 @@ func (s *EthereumAPI) Syncing() (interface{}, error) { return false, nil } +// GetChainConfig returns the chain config. +func (s *EthereumAPI) GetChainConfig(ctx context.Context) *params.ChainConfig { + return s.b.ChainConfig() +} + // TxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. type TxPoolAPI struct { b Backend @@ -1006,7 +1012,7 @@ func newRevertError(result *core.ExecutionResult) *revertError { } } -// revertError is an API error that encompassas an EVM revertal with JSON error +// revertError is an API error that encompasses an EVM revertal with JSON error // code and a binary data blob. type revertError struct { error @@ -1237,6 +1243,9 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { if head.BlockGasCost != nil { result["blockGasCost"] = (*hexutil.Big)(head.BlockGasCost) } + if head.ExtraStateRoot != (common.Hash{}) { + result["extraStateRoot"] = head.ExtraStateRoot + } return result } @@ -1455,9 +1464,11 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH if db == nil || err != nil { return nil, 0, nil, err } - // If the gas amount is not set, extract this as it will depend on access - // lists and we'll need to reestimate every time - nogas := args.Gas == nil + // If the gas amount is not set, default to RPC gas cap. + if args.Gas == nil { + tmp := hexutil.Uint64(b.RPCGasCap()) + args.Gas = &tmp + } // Ensure any missing fields are filled, extract the recipient and input data if err := args.setDefaults(ctx, b); err != nil { @@ -1482,15 +1493,6 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH accessList := prevTracer.AccessList() log.Trace("Creating access list", "input", accessList) - // If no gas amount was specified, each unique access list needs it's own - // gas calculation. This is quite expensive, but we need to be accurate - // and it's convered by the sender only anyway. - if nogas { - args.Gas = nil - if err := args.setDefaults(ctx, b); err != nil { - return nil, 0, nil, err // shouldn't happen, just in case - } - } // Copy the original db so we don't modify it statedb := db.Copy() // Set the access list tracer to the last al @@ -1519,6 +1521,49 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH } } +// Note: this API is moved directly from ./eth/api.go to ensure that it is available under an API that is enabled by +// default without duplicating the code and serving the same API in the original location as well without creating a +// cyclic import. +// +// BadBlockArgs represents the entries in the list returned when bad blocks are queried. +type BadBlockArgs struct { + Hash common.Hash `json:"hash"` + Block map[string]interface{} `json:"block"` + RLP string `json:"rlp"` + Reason *core.BadBlockReason `json:"reason"` +} + +// GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network +// and returns them as a JSON list of block hashes. +func (s *BlockChainAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) { + var ( + err error + badBlocks, reasons = s.b.BadBlocks() + results = make([]*BadBlockArgs, 0, len(badBlocks)) + ) + for i, block := range badBlocks { + var ( + blockRlp string + blockJSON map[string]interface{} + ) + if rlpBytes, err := rlp.EncodeToBytes(block); err != nil { + blockRlp = err.Error() // Hacky, but hey, it works + } else { + blockRlp = fmt.Sprintf("%#x", rlpBytes) + } + if blockJSON, err = RPCMarshalBlock(block, true, true, s.b.ChainConfig()); err != nil { + blockJSON = map[string]interface{}{"error": err.Error()} + } + results = append(results, &BadBlockArgs{ + Hash: block.Hash(), + RLP: blockRlp, + Block: blockJSON, + Reason: reasons[i], + }) + } + return results, nil +} + // TransactionAPI exposes methods for reading and creating transaction data. type TransactionAPI struct { b Backend diff --git a/coreth/internal/ethapi/backend.go b/coreth/internal/ethapi/backend.go index 914aa1fb..75b6af60 100644 --- a/coreth/internal/ethapi/backend.go +++ b/coreth/internal/ethapi/backend.go @@ -35,10 +35,10 @@ import ( "github.com/ava-labs/coreth/accounts" "github.com/ava-labs/coreth/consensus" "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/core/bloombits" "github.com/ava-labs/coreth/core/state" "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/coreth/core/vm" + "github.com/ava-labs/coreth/eth/filters" "github.com/ava-labs/coreth/ethdb" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/rpc" @@ -78,6 +78,7 @@ type Backend interface { SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription + BadBlocks() ([]*types.Block, []*core.BadBlockReason) // Transaction pool API SendTx(ctx context.Context, signedTx *types.Transaction) error @@ -90,18 +91,13 @@ type Backend interface { TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription - // Filter API - BloomStatus() (uint64, uint64) - GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) - ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) - SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription - SubscribeAcceptedLogsEvent(ch chan<- []*types.Log) event.Subscription - SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription - SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription - ChainConfig() *params.ChainConfig Engine() consensus.Engine LastAcceptedBlock() *types.Block + + // eth/filters needs to be initialized from this backend type, so methods needed by + // it must also be included here. + filters.Backend } func GetAPIs(apiBackend Backend) []rpc.API { diff --git a/coreth/internal/ethapi/transaction_args.go b/coreth/internal/ethapi/transaction_args.go index eeed60a5..8d1876c4 100644 --- a/coreth/internal/ethapi/transaction_args.go +++ b/coreth/internal/ethapi/transaction_args.go @@ -34,6 +34,7 @@ import ( "math/big" "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/rpc" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -85,57 +86,10 @@ func (args *TransactionArgs) data() []byte { // setDefaults fills in default values for unspecified tx fields. func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { - if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { - return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } - // After london, default to 1559 unless gasPrice is set - head := b.CurrentHeader() - // If user specifies both maxPriorityfee and maxFee, then we do not - // need to consult the chain for defaults. It's definitely a London tx. - if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { - // In this clause, user left some fields unspecified. - if b.ChainConfig().IsApricotPhase3(new(big.Int).SetUint64(head.Time)) && args.GasPrice == nil { - if args.MaxPriorityFeePerGas == nil { - tip, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err - } - args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) - } - if args.MaxFeePerGas == nil { - gasFeeCap := new(big.Int).Add( - (*big.Int)(args.MaxPriorityFeePerGas), - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), - ) - args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) - } - if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) - } - } else { - if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { - return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") - } - if args.GasPrice == nil { - price, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err - } - if b.ChainConfig().IsApricotPhase3(new(big.Int).SetUint64(head.Time)) { - // The legacy tx gas price suggestion should not add 2x base fee - // because all fees are consumed, so it would result in a spiral - // upwards. - price.Add(price, head.BaseFee) - } - args.GasPrice = (*hexutil.Big)(price) - } - } - } else { - // Both maxPriorityfee and maxFee set by caller. Sanity-check their internal relation - if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) - } + if err := args.setFeeDefaults(ctx, b); err != nil { + return err } + if args.Value == nil { args.Value = new(hexutil.Big) } @@ -188,6 +142,79 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { return nil } +type feeBackend interface { + SuggestGasTipCap(ctx context.Context) (*big.Int, error) + CurrentHeader() *types.Header + ChainConfig() *params.ChainConfig +} + +// setFeeDefaults fills in default fee values for unspecified tx fields. +func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) error { + // If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error. + if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { + return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + // If the tx has completely specified a fee mechanism, no default is needed. This allows users + // who are not yet synced past London to get defaults for other tx values. See + // https://github.com/ethereum/go-ethereum/pull/23274 for more information. + eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil + if (args.GasPrice != nil && !eip1559ParamsSet) || (args.GasPrice == nil && eip1559ParamsSet) { + // Sanity check the EIP-1559 fee parameters if present. + if args.GasPrice == nil && args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + return nil + } + // Now attempt to fill in default value depending on whether London is active or not. + head := b.CurrentHeader() + if b.ChainConfig().IsApricotPhase3(new(big.Int).SetUint64(head.Time)) { + // London is active, set maxPriorityFeePerGas and maxFeePerGas. + if err := args.setApricotPhase3FeeDefault(ctx, head, b); err != nil { + return err + } + } else { + if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { + return errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active") + } + if args.GasPrice == nil { + price, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + args.GasPrice = (*hexutil.Big)(price) + } + } + return nil +} + +// setApricotPhase3FeeDefault fills in reasonable default fee values for unspecified fields. +func (args *TransactionArgs) setApricotPhase3FeeDefault(ctx context.Context, head *types.Header, b feeBackend) error { + // Set maxPriorityFeePerGas if it is missing. + if args.MaxPriorityFeePerGas == nil { + tip, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) + } + // Set maxFeePerGas if it is missing. + if args.MaxFeePerGas == nil { + // Set the max fee to be 2 times larger than the previous block's base fee. + // The additional slack allows the tx to not become invalidated if the base + // fee is rising. + gasFeeCap := new(big.Int).Add( + (*big.Int)(args.MaxPriorityFeePerGas), + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) + args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) + } + // Both EIP-1559 fee parameters are now set; sanity check them. + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + return nil +} + // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. diff --git a/coreth/internal/ethapi/transaction_args_test.go b/coreth/internal/ethapi/transaction_args_test.go new file mode 100644 index 00000000..e29098ac --- /dev/null +++ b/coreth/internal/ethapi/transaction_args_test.go @@ -0,0 +1,257 @@ +// (c) 2022, Ava Labs, Inc. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "context" + "fmt" + "math/big" + "reflect" + "testing" + + "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/params" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ feeBackend = &backendMock{} + +// TestSetFeeDefaults tests the logic for filling in default fee values works as expected. +func TestSetFeeDefaults(t *testing.T) { + type test struct { + name string + isLondon bool + in *TransactionArgs + want *TransactionArgs + err error + } + + var ( + b = newBackendMock() + fortytwo = (*hexutil.Big)(big.NewInt(42)) + maxFee = (*hexutil.Big)(new(big.Int).Add(new(big.Int).Mul(b.current.BaseFee, big.NewInt(2)), fortytwo.ToInt())) + al = &types.AccessList{types.AccessTuple{Address: common.Address{0xaa}, StorageKeys: []common.Hash{{0x01}}}} + ) + + tests := []test{ + // Legacy txs + { + "legacy tx pre-London", + false, + &TransactionArgs{}, + &TransactionArgs{GasPrice: fortytwo}, + nil, + }, + { + "legacy tx post-London, explicit gas price", + true, + &TransactionArgs{GasPrice: fortytwo}, + &TransactionArgs{GasPrice: fortytwo}, + nil, + }, + + // Access list txs + { + "access list tx pre-London", + false, + &TransactionArgs{AccessList: al}, + &TransactionArgs{AccessList: al, GasPrice: fortytwo}, + nil, + }, + { + "access list tx post-London, explicit gas price", + false, + &TransactionArgs{AccessList: al, GasPrice: fortytwo}, + &TransactionArgs{AccessList: al, GasPrice: fortytwo}, + nil, + }, + { + "access list tx post-London", + true, + &TransactionArgs{AccessList: al}, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "access list tx post-London, only max fee", + true, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee}, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "access list tx post-London, only priority fee", + true, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee}, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + + // Dynamic fee txs + { + "dynamic tx post-London", + true, + &TransactionArgs{}, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "dynamic tx post-London, only max fee", + true, + &TransactionArgs{MaxFeePerGas: maxFee}, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "dynamic tx post-London, only priority fee", + true, + &TransactionArgs{MaxFeePerGas: maxFee}, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "dynamic fee tx pre-London, maxFee set", + false, + &TransactionArgs{MaxFeePerGas: maxFee}, + nil, + fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), + }, + { + "dynamic fee tx pre-London, priorityFee set", + false, + &TransactionArgs{MaxPriorityFeePerGas: fortytwo}, + nil, + fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), + }, + { + "dynamic fee tx, maxFee < priorityFee", + true, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(1000))}, + nil, + fmt.Errorf("maxFeePerGas (0x3e) < maxPriorityFeePerGas (0x3e8)"), + }, + { + "dynamic fee tx, maxFee < priorityFee while setting default", + true, + &TransactionArgs{MaxFeePerGas: (*hexutil.Big)(big.NewInt(7))}, + nil, + fmt.Errorf("maxFeePerGas (0x7) < maxPriorityFeePerGas (0x2a)"), + }, + + // Misc + { + "set all fee parameters", + false, + &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), + }, + { + "set gas price and maxPriorityFee", + false, + &TransactionArgs{GasPrice: fortytwo, MaxPriorityFeePerGas: fortytwo}, + nil, + fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), + }, + { + "set gas price and maxFee", + true, + &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee}, + nil, + fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), + }, + } + + ctx := context.Background() + for i, test := range tests { + if test.isLondon { + b.activateLondon() + } else { + b.deactivateLondon() + } + got := test.in + err := got.setFeeDefaults(ctx, b) + if err != nil && err.Error() == test.err.Error() { + // Test threw expected error. + continue + } else if err != nil { + t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err) + } + if !reflect.DeepEqual(got, test.want) { + t.Fatalf("test %d (%s): did not fill defaults as expected: (got: %v, want: %v)", i, test.name, got, test.want) + } + } +} + +type backendMock struct { + current *types.Header + config *params.ChainConfig +} + +func newBackendMock() *backendMock { + config := ¶ms.ChainConfig{ + ChainID: big.NewInt(42), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(1000), + } + return &backendMock{ + current: &types.Header{ + Difficulty: big.NewInt(10000000000), + Number: big.NewInt(1100), + GasLimit: 8_000_000, + GasUsed: 8_000_000, + Time: 555, + Extra: make([]byte, 32), + BaseFee: big.NewInt(10), + }, + config: config, + } +} + +func (b *backendMock) activateLondon() { + b.current.Time = uint64(1100) +} + +func (b *backendMock) deactivateLondon() { + b.current.Time = uint64(900) +} +func (b *backendMock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return big.NewInt(42), nil +} +func (b *backendMock) CurrentHeader() *types.Header { return b.current } +func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config } diff --git a/coreth/metrics/gauge_float64_test.go b/coreth/metrics/gauge_float64_test.go index 02b75580..7b854d23 100644 --- a/coreth/metrics/gauge_float64_test.go +++ b/coreth/metrics/gauge_float64_test.go @@ -2,7 +2,7 @@ package metrics import "testing" -func BenchmarkGuageFloat64(b *testing.B) { +func BenchmarkGaugeFloat64(b *testing.B) { g := NewGaugeFloat64() b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/coreth/metrics/gauge_test.go b/coreth/metrics/gauge_test.go index 3aee1434..a98fe985 100644 --- a/coreth/metrics/gauge_test.go +++ b/coreth/metrics/gauge_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func BenchmarkGuage(b *testing.B) { +func BenchmarkGauge(b *testing.B) { g := NewGauge() b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/coreth/metrics/runtime_test.go b/coreth/metrics/runtime_test.go index f85f7868..ddc49f03 100644 --- a/coreth/metrics/runtime_test.go +++ b/coreth/metrics/runtime_test.go @@ -16,6 +16,7 @@ func BenchmarkRuntimeMemStats(b *testing.B) { } func TestRuntimeMemStats(t *testing.T) { + t.Skip("FLAKY") r := NewRegistry() RegisterRuntimeMemStats(r) CaptureRuntimeMemStatsOnce(r) @@ -48,6 +49,7 @@ func TestRuntimeMemStats(t *testing.T) { } func TestRuntimeMemStatsNumThread(t *testing.T) { + t.Skip("FLAKY") r := NewRegistry() RegisterRuntimeMemStats(r) CaptureRuntimeMemStatsOnce(r) @@ -58,6 +60,7 @@ func TestRuntimeMemStatsNumThread(t *testing.T) { } func TestRuntimeMemStatsBlocking(t *testing.T) { + t.Skip("FLAKY") if g := runtime.GOMAXPROCS(0); g < 2 { t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g) } diff --git a/coreth/miner/worker.go b/coreth/miner/worker.go index 0d349031..da3f85d8 100644 --- a/coreth/miner/worker.go +++ b/coreth/miner/worker.go @@ -45,6 +45,7 @@ import ( "github.com/ava-labs/coreth/core/state" "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -299,6 +300,10 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) txs.Pop() + case errors.Is(err, vmerrs.ErrToAddrProhibitedSoft): + log.Warn("Tx dropped: failed verification", "tx", tx.Hash(), "sender", from, "data", tx.Data(), "err", err) + w.eth.TxPool().RemoveTx(tx.Hash()) + txs.Pop() default: // Strange error, discard the transaction and get the next in line (note, the // nonce-too-high clause will prevent us from executing in vain). diff --git a/coreth/params/config.go b/coreth/params/config.go index 2350b550..d4ec0cb1 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -61,151 +61,280 @@ var ( var ( // AvalancheMainnetChainConfig is the configuration for Avalanche Main Network AvalancheMainnetChainConfig = &ChainConfig{ - ChainID: AvalancheMainnetChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2021, time.March, 31, 14, 0, 0, 0, time.UTC).Unix()), - ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2021, time.May, 10, 11, 0, 0, 0, time.UTC).Unix()), - ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2021, time.August, 24, 14, 0, 0, 0, time.UTC).Unix()), - ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2021, time.September, 22, 21, 0, 0, 0, time.UTC).Unix()), - ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2021, time.December, 2, 18, 0, 0, 0, time.UTC).Unix()), - // TODO Add Blueberry + ChainID: AvalancheMainnetChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2021, time.March, 31, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2021, time.May, 10, 11, 0, 0, 0, time.UTC).Unix()), + ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2021, time.August, 24, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2021, time.September, 22, 21, 0, 0, 0, time.UTC).Unix()), + ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2021, time.December, 2, 18, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(2022, time.September, 5, 1, 30, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(2022, time.September, 6, 20, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(2022, time.September, 7, 3, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(2022, time.October, 18, 16, 0, 0, 0, time.UTC).Unix()), + // TODO Add Banff and Cortina timestamps } // AvalancheFujiChainConfig is the configuration for the Fuji Test Network AvalancheFujiChainConfig = &ChainConfig{ - ChainID: AvalancheFujiChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2021, time.March, 26, 14, 0, 0, 0, time.UTC).Unix()), - ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2021, time.May, 5, 14, 0, 0, 0, time.UTC).Unix()), - ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2021, time.August, 16, 19, 0, 0, 0, time.UTC).Unix()), - ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2021, time.September, 16, 21, 0, 0, 0, time.UTC).Unix()), - ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2021, time.November, 24, 15, 0, 0, 0, time.UTC).Unix()), - // TODO Add Blueberry + ChainID: AvalancheFujiChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2021, time.March, 26, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2021, time.May, 5, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2021, time.August, 16, 19, 0, 0, 0, time.UTC).Unix()), + ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2021, time.September, 16, 21, 0, 0, 0, time.UTC).Unix()), + ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2021, time.November, 24, 15, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(2022, time.September, 6, 20, 0, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(2022, time.September, 6, 20, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(2022, time.September, 7, 6, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(2022, time.October, 3, 14, 0, 0, 0, time.UTC).Unix()), + // TODO add Cortina timestamp } // AvalancheLocalChainConfig is the configuration for the Avalanche Local Network AvalancheLocalChainConfig = &ChainConfig{ - ChainID: AvalancheLocalChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotPhase1BlockTimestamp: big.NewInt(0), - ApricotPhase2BlockTimestamp: big.NewInt(0), - ApricotPhase3BlockTimestamp: big.NewInt(0), - ApricotPhase4BlockTimestamp: big.NewInt(0), - ApricotPhase5BlockTimestamp: big.NewInt(0), - BlueberryBlockTimestamp: big.NewInt(0), + ChainID: AvalancheLocalChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + ApricotPhasePre6BlockTimestamp: big.NewInt(0), + ApricotPhase6BlockTimestamp: big.NewInt(0), + ApricotPhasePost6BlockTimestamp: big.NewInt(0), + BanffBlockTimestamp: big.NewInt(0), + } + + FlareChainConfig = &ChainConfig{ + ChainID: FlareChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + } + + CostwoChainConfig = &ChainConfig{ + ChainID: CostwoChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + } + + StagingChainConfig = &ChainConfig{ + ChainID: StagingChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + } + + LocalFlareChainConfig = &ChainConfig{ + ChainID: LocalFlareChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + ApricotPhasePre6BlockTimestamp: big.NewInt(0), + ApricotPhase6BlockTimestamp: big.NewInt(0), + ApricotPhasePost6BlockTimestamp: big.NewInt(0), + BanffBlockTimestamp: big.NewInt(time.Date(2024, time.May, 29, 9, 15, 0, 0, time.UTC).Unix()), } // CostonChainConfig is the configuration for the Coston test network. CostonChainConfig = &ChainConfig{ - ChainID: CostonChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), - ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), - ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 14, 0, 0, 0, time.UTC).Unix()), - ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 15, 0, 0, 0, time.UTC).Unix()), - ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 16, 0, 0, 0, time.UTC).Unix()), - SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.May, 14, 12, 30, 0, 0, time.UTC).Unix()), + ChainID: CostonChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 15, 0, 0, 0, time.UTC).Unix()), + ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.February, 25, 16, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.July, 23, 12, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), } // LocalChainConfig is the configuration for the Songbird Local network. LocalChainConfig = &ChainConfig{ - ChainID: LocalChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotPhase1BlockTimestamp: big.NewInt(0), - ApricotPhase2BlockTimestamp: big.NewInt(0), - ApricotPhase3BlockTimestamp: big.NewInt(0), - ApricotPhase4BlockTimestamp: big.NewInt(0), - ApricotPhase5BlockTimestamp: big.NewInt(0), - SongbirdTransitionTimestamp: big.NewInt(0), + ChainID: LocalChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + ApricotPhasePre6BlockTimestamp: big.NewInt(0), + ApricotPhase6BlockTimestamp: big.NewInt(0), + ApricotPhasePost6BlockTimestamp: big.NewInt(0), + SongbirdTransitionTimestamp: big.NewInt(0), + BanffBlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + // TODO add Cortina timestamp } // SongbirdChainConfig is the configuration for the Songbird canary network. SongbirdChainConfig = &ChainConfig{ - ChainID: SongbirdChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), - ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), - ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 14, 0, 0, 0, time.UTC).Unix()), - ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 15, 0, 0, 0, time.UTC).Unix()), - ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 16, 0, 0, 0, time.UTC).Unix()), - SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.May, 25, 0, 0, 0, 0, time.UTC).Unix()), - } - - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} - TestLaunchConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil} - TestApricotPhase1Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil} - TestApricotPhase2Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil} - TestApricotPhase3Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil} - TestApricotPhase4Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil} - TestApricotPhase5Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil} - TestBlueberryChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil} - TestRules = TestChainConfig.AvalancheRules(new(big.Int), new(big.Int)) + ChainID: SongbirdChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 14, 0, 0, 0, time.UTC).Unix()), + ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 15, 0, 0, 0, time.UTC).Unix()), + ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2022, time.March, 7, 16, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + SongbirdTransitionTimestamp: big.NewInt(time.Date(2024, time.August, 20, 12, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + } + + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} + TestLaunchConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil} + TestApricotPhase1Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil} + TestApricotPhase2Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil, nil, nil} + TestApricotPhase3Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil, nil} + TestApricotPhase4Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil} + TestApricotPhase5Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil} + TestApricotPhasePre6Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil} + TestApricotPhase6Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil} + TestApricotPhasePost6Config = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil} + TestSgbTransitionChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil} + TestBanffChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil} + TestCortinaChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} + TestRules = TestChainConfig.AvalancheRules(new(big.Int), new(big.Int)) ) // ChainConfig is the core config which determines the blockchain settings. @@ -245,11 +374,18 @@ type ChainConfig struct { ApricotPhase4BlockTimestamp *big.Int `json:"apricotPhase4BlockTimestamp,omitempty"` // Apricot Phase 5 introduces a batch of atomic transactions with a maximum atomic gas limit per block. (nil = no fork, 0 = already activated) ApricotPhase5BlockTimestamp *big.Int `json:"apricotPhase5BlockTimestamp,omitempty"` - // Blueberry TODO comment. (nil = no fork, 0 = already activated) - BlueberryBlockTimestamp *big.Int `json:"blueberryBlockTimestamp,omitempty"` - - // SGB-MERGE: when export/import transactions will be allowed on songbird code (Songbird, Coston, Local) + // Apricot Phase Pre-6 deprecates the NativeAssetCall precompile (soft). (nil = no fork, 0 = already activated) + ApricotPhasePre6BlockTimestamp *big.Int `json:"apricotPhasePre6BlockTimestamp,omitempty"` + // Apricot Phase 6 deprecates the NativeAssetBalance and NativeAssetCall precompiles. (nil = no fork, 0 = already activated) + ApricotPhase6BlockTimestamp *big.Int `json:"apricotPhase6BlockTimestamp,omitempty"` + // Apricot Phase Post-6 deprecates the NativeAssetCall precompile (soft). (nil = no fork, 0 = already activated) + ApricotPhasePost6BlockTimestamp *big.Int `json:"apricotPhasePost6BlockTimestamp,omitempty"` + // When export/import transactions will be allowed on songbird code (Songbird, Coston, Local) SongbirdTransitionTimestamp *big.Int `json:"songbirdTransitionTimestamp,omitempty"` + // Banff TODO comment. (nil = no fork, 0 = already activated) + BanffBlockTimestamp *big.Int `json:"banffBlockTimestamp,omitempty"` + // Cortina TODO comment. (nil = no fork, 0 = already activated) + CortinaBlockTimestamp *big.Int `json:"cortinaBlockTimestamp,omitempty"` } // String implements the fmt.Stringer interface. @@ -277,19 +413,23 @@ func (c *ChainConfig) String() string { if c.MuirGlacierBlock != nil { banner += fmt.Sprintf(" - Muir Glacier: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md)\n", c.MuirGlacierBlock) } - banner += fmt.Sprintf(" - Apricot Phase 1 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.3.0)\n", c.ApricotPhase1BlockTimestamp) - banner += fmt.Sprintf(" - Apricot Phase 2 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.4.0)\n", c.ApricotPhase2BlockTimestamp) - banner += fmt.Sprintf(" - Apricot Phase 3 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.5.0)\n", c.ApricotPhase3BlockTimestamp) - banner += fmt.Sprintf(" - Apricot Phase 4 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.6.0)\n", c.ApricotPhase4BlockTimestamp) - banner += fmt.Sprintf(" - Apricot Phase 5 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.7.0)\n", c.ApricotPhase5BlockTimestamp) - banner += fmt.Sprintf(" - Bluberry Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.8.0)\n", c.BlueberryBlockTimestamp) - banner += fmt.Sprintf(" - Sgb Transition Timestamp: %-8v\n", c.SongbirdTransitionTimestamp) + banner += fmt.Sprintf(" - Apricot Phase 1 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.3.0)\n", c.ApricotPhase1BlockTimestamp) + banner += fmt.Sprintf(" - Apricot Phase 2 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.4.0)\n", c.ApricotPhase2BlockTimestamp) + banner += fmt.Sprintf(" - Apricot Phase 3 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.5.0)\n", c.ApricotPhase3BlockTimestamp) + banner += fmt.Sprintf(" - Apricot Phase 4 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.6.0)\n", c.ApricotPhase4BlockTimestamp) + banner += fmt.Sprintf(" - Apricot Phase 5 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.7.0)\n", c.ApricotPhase5BlockTimestamp) + banner += fmt.Sprintf(" - Apricot Phase P6 Timestamp %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.8.0)\n", c.ApricotPhasePre6BlockTimestamp) + banner += fmt.Sprintf(" - Apricot Phase 6 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.8.0)\n", c.ApricotPhase6BlockTimestamp) + banner += fmt.Sprintf(" - Apricot Phase Post-6 Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.8.0\n", c.ApricotPhasePost6BlockTimestamp) + banner += fmt.Sprintf(" - Songbird Transition Timestamp: %-8v\n", c.SongbirdTransitionTimestamp) + banner += fmt.Sprintf(" - Banff Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.9.0)\n", c.BanffBlockTimestamp) + banner += fmt.Sprintf(" - Cortina Timestamp: %-8v (https://github.com/ava-labs/avalanchego/releases/tag/v1.10.0)\n", c.CortinaBlockTimestamp) banner += "\n" return banner } // SGB-MERGE -// Code for songbird network (Songbird, Coston, Local id) +// Code for songbird network (songbird, coston, local id) func (c *ChainConfig) IsSongbirdCode() bool { return c.ChainID != nil && (c.ChainID.Cmp(SongbirdChainID) == 0 || c.ChainID.Cmp(CostonChainID) == 0 || c.ChainID.Cmp(LocalChainID) == 0) } @@ -378,10 +518,22 @@ func (c *ChainConfig) IsApricotPhase5(blockTimestamp *big.Int) bool { return utils.IsForked(c.ApricotPhase5BlockTimestamp, blockTimestamp) } -// IsBlueberry returns whether [blockTimestamp] represents a block -// with a timestamp after the Blueberry upgrade time. -func (c *ChainConfig) IsBlueberry(blockTimestamp *big.Int) bool { - return utils.IsForked(c.BlueberryBlockTimestamp, blockTimestamp) +// IsApricotPhasePre6 returns whether [blockTimestamp] represents a block +// with a timestamp after the Apricot Phase Pre 6 upgrade time. +func (c *ChainConfig) IsApricotPhasePre6(blockTimestamp *big.Int) bool { + return utils.IsForked(c.ApricotPhasePre6BlockTimestamp, blockTimestamp) +} + +// IsApricotPhase6 returns whether [blockTimestamp] represents a block +// with a timestamp after the Apricot Phase 6 upgrade time. +func (c *ChainConfig) IsApricotPhase6(blockTimestamp *big.Int) bool { + return utils.IsForked(c.ApricotPhase6BlockTimestamp, blockTimestamp) +} + +// IsApricotPhasePost6 returns whether [blockTimestamp] represents a block +// with a timestamp after the Apricot Phase 6 Post upgrade time. +func (c *ChainConfig) IsApricotPhasePost6(blockTimestamp *big.Int) bool { + return utils.IsForked(c.ApricotPhasePost6BlockTimestamp, blockTimestamp) } // IsSongbirdTransition returns whether [blockTimestamp] represents a block @@ -390,21 +542,33 @@ func (c *ChainConfig) IsSongbirdTransition(blockTimestamp *big.Int) bool { return utils.IsForked(c.SongbirdTransitionTimestamp, blockTimestamp) } +// IsBanff returns whether [blockTimestamp] represents a block +// with a timestamp after the Banff upgrade time. +func (c *ChainConfig) IsBanff(blockTimestamp *big.Int) bool { + return utils.IsForked(c.BanffBlockTimestamp, blockTimestamp) +} + +// IsCortina returns whether [blockTimestamp] represents a block +// with a timestamp after the Cortina upgrade time. +func (c *ChainConfig) IsCortina(blockTimestamp *big.Int) bool { + return utils.IsForked(c.CortinaBlockTimestamp, blockTimestamp) +} + // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, timestamp uint64) *ConfigCompatError { - bhead := new(big.Int).SetUint64(height) - bheadTimestamp := new(big.Int).SetUint64(timestamp) + bNumber := new(big.Int).SetUint64(height) + bTimestamp := new(big.Int).SetUint64(timestamp) // Iterate checkCompatible to find the lowest conflict. var lasterr *ConfigCompatError for { - err := c.checkCompatible(newcfg, bhead, bheadTimestamp) + err := c.checkCompatible(newcfg, bNumber, bTimestamp) if err == nil || (lasterr != nil && err.RewindTo == lasterr.RewindTo) { break } lasterr = err - bhead.SetUint64(err.RewindTo) + bNumber.SetUint64(err.RewindTo) } return lasterr } @@ -464,6 +628,12 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "apricotPhase2BlockTimestamp", block: c.ApricotPhase2BlockTimestamp}, {name: "apricotPhase3BlockTimestamp", block: c.ApricotPhase3BlockTimestamp}, {name: "apricotPhase4BlockTimestamp", block: c.ApricotPhase4BlockTimestamp}, + {name: "apricotPhase5BlockTimestamp", block: c.ApricotPhase5BlockTimestamp}, + {name: "apricotPhasePre6BlockTimestamp", block: c.ApricotPhasePre6BlockTimestamp}, + {name: "apricotPhase6BlockTimestamp", block: c.ApricotPhase6BlockTimestamp}, + {name: "apricotPhasePost6BlockTimestamp", block: c.ApricotPhasePost6BlockTimestamp}, + {name: "banffBlockTimestamp", block: c.BanffBlockTimestamp}, + {name: "cortinaBlockTimestamp", block: c.CortinaBlockTimestamp}, } { if lastFork.name != "" { // Next one must be higher number @@ -490,65 +660,71 @@ func (c *ChainConfig) CheckConfigForkOrder() error { return nil } -func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headHeight *big.Int, headTimestamp *big.Int) *ConfigCompatError { - if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, headHeight) { +func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, lastHeight *big.Int, lastTimestamp *big.Int) *ConfigCompatError { + if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, lastHeight) { return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) } - if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, headHeight) { + if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, lastHeight) { return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock) } - if c.IsDAOFork(headHeight) && c.DAOForkSupport != newcfg.DAOForkSupport { + if c.IsDAOFork(lastHeight) && c.DAOForkSupport != newcfg.DAOForkSupport { return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock) } - if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, headHeight) { + if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, lastHeight) { return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block) } - if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, headHeight) { + if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, lastHeight) { return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block) } - if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, headHeight) { + if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, lastHeight) { return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block) } - if c.IsEIP158(headHeight) && !configNumEqual(c.ChainID, newcfg.ChainID) { + if c.IsEIP158(lastHeight) && !configNumEqual(c.ChainID, newcfg.ChainID) { return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block) } - if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, headHeight) { + if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, lastHeight) { return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock) } - if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, headHeight) { + if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, lastHeight) { return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) } - if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, headHeight) { + if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, lastHeight) { // the only case where we allow Petersburg to be set in the past is if it is equal to Constantinople // mainly to satisfy fork ordering requirements which state that Petersburg fork be set if Constantinople fork is set - if isForkIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, headHeight) { + if isForkIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, lastHeight) { return newCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock) } } - if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, headHeight) { + if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, lastHeight) { return newCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock) } - if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, headHeight) { + if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, lastHeight) { return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock) } - if isForkIncompatible(c.ApricotPhase1BlockTimestamp, newcfg.ApricotPhase1BlockTimestamp, headTimestamp) { + if isForkIncompatible(c.ApricotPhase1BlockTimestamp, newcfg.ApricotPhase1BlockTimestamp, lastTimestamp) { return newCompatError("ApricotPhase1 fork block timestamp", c.ApricotPhase1BlockTimestamp, newcfg.ApricotPhase1BlockTimestamp) } - if isForkIncompatible(c.ApricotPhase2BlockTimestamp, newcfg.ApricotPhase2BlockTimestamp, headTimestamp) { + if isForkIncompatible(c.ApricotPhase2BlockTimestamp, newcfg.ApricotPhase2BlockTimestamp, lastTimestamp) { return newCompatError("ApricotPhase2 fork block timestamp", c.ApricotPhase2BlockTimestamp, newcfg.ApricotPhase2BlockTimestamp) } - if isForkIncompatible(c.ApricotPhase3BlockTimestamp, newcfg.ApricotPhase3BlockTimestamp, headTimestamp) { + if isForkIncompatible(c.ApricotPhase3BlockTimestamp, newcfg.ApricotPhase3BlockTimestamp, lastTimestamp) { return newCompatError("ApricotPhase3 fork block timestamp", c.ApricotPhase3BlockTimestamp, newcfg.ApricotPhase3BlockTimestamp) } - if isForkIncompatible(c.ApricotPhase4BlockTimestamp, newcfg.ApricotPhase4BlockTimestamp, headTimestamp) { + if isForkIncompatible(c.ApricotPhase4BlockTimestamp, newcfg.ApricotPhase4BlockTimestamp, lastTimestamp) { return newCompatError("ApricotPhase4 fork block timestamp", c.ApricotPhase4BlockTimestamp, newcfg.ApricotPhase4BlockTimestamp) } - if isForkIncompatible(c.ApricotPhase5BlockTimestamp, newcfg.ApricotPhase5BlockTimestamp, headTimestamp) { + if isForkIncompatible(c.ApricotPhase5BlockTimestamp, newcfg.ApricotPhase5BlockTimestamp, lastTimestamp) { return newCompatError("ApricotPhase5 fork block timestamp", c.ApricotPhase5BlockTimestamp, newcfg.ApricotPhase5BlockTimestamp) } - if isForkIncompatible(c.BlueberryBlockTimestamp, newcfg.BlueberryBlockTimestamp, headTimestamp) { - return newCompatError("Blueberry fork block timestamp", c.BlueberryBlockTimestamp, newcfg.BlueberryBlockTimestamp) - } + // TODO: add Phase 6 checks + + // TODO activate isForkIncompatible checks + // if isForkIncompatible(c.BanffBlockTimestamp, newcfg.BanffBlockTimestamp, lastTimestamp) { + // return newCompatError("Banff fork block timestamp", c.BanffBlockTimestamp, newcfg.BanffBlockTimestamp) + // } + // if isForkIncompatible(c.CortinaBlockTimestamp, newcfg.CortinaBlockTimestamp, lastTimestamp) { + // return newCompatError("Cortina fork block timestamp", c.CortinaBlockTimestamp, newcfg.CortinaBlockTimestamp) + // } return nil } @@ -611,10 +787,13 @@ type Rules struct { // Rules for Avalanche releases IsApricotPhase1, IsApricotPhase2, IsApricotPhase3, IsApricotPhase4, IsApricotPhase5 bool - IsBlueberry bool + IsApricotPhasePre6, IsApricotPhase6, IsApricotPhasePost6 bool + IsBanff bool + IsCortina bool + IsClementine bool // SGB-MERGE - // Songbird (Songbird, Coston, Local) + // Songbird (coston, local) IsSongbirdCode bool IsSongbirdTransition bool @@ -655,8 +834,12 @@ func (c *ChainConfig) AvalancheRules(blockNum, blockTimestamp *big.Int) Rules { rules.IsApricotPhase3 = c.IsApricotPhase3(blockTimestamp) rules.IsApricotPhase4 = c.IsApricotPhase4(blockTimestamp) rules.IsApricotPhase5 = c.IsApricotPhase5(blockTimestamp) - rules.IsBlueberry = c.IsBlueberry(blockTimestamp) + rules.IsApricotPhasePre6 = c.IsApricotPhasePre6(blockTimestamp) + rules.IsApricotPhase6 = c.IsApricotPhase6(blockTimestamp) + rules.IsApricotPhasePost6 = c.IsApricotPhasePost6(blockTimestamp) rules.IsSongbirdTransition = c.IsSongbirdTransition(blockTimestamp) + rules.IsBanff = c.IsBanff(blockTimestamp) + rules.IsCortina = c.IsCortina(blockTimestamp) // Initialize the stateful precompiles that should be enabled at [blockTimestamp]. rules.Precompiles = make(map[common.Address]precompile.StatefulPrecompiledContract) diff --git a/coreth/params/config_test.go b/coreth/params/config_test.go index 62fea4be..0f242adc 100644 --- a/coreth/params/config_test.go +++ b/coreth/params/config_test.go @@ -34,25 +34,25 @@ import ( func TestCheckCompatible(t *testing.T) { type test struct { - stored, new *ChainConfig - headHeight, headTimestamp uint64 - wantErr *ConfigCompatError + stored, new *ChainConfig + blockHeight, blockTimestamp uint64 + wantErr *ConfigCompatError } tests := []test{ - {stored: TestChainConfig, new: TestChainConfig, headHeight: 0, headTimestamp: 0, wantErr: nil}, - {stored: TestChainConfig, new: TestChainConfig, headHeight: 100, headTimestamp: 1000, wantErr: nil}, + {stored: TestChainConfig, new: TestChainConfig, blockHeight: 0, blockTimestamp: 0, wantErr: nil}, + {stored: TestChainConfig, new: TestChainConfig, blockHeight: 100, blockTimestamp: 1000, wantErr: nil}, { - stored: &ChainConfig{EIP150Block: big.NewInt(10)}, - new: &ChainConfig{EIP150Block: big.NewInt(20)}, - headHeight: 9, - headTimestamp: 90, - wantErr: nil, + stored: &ChainConfig{EIP150Block: big.NewInt(10)}, + new: &ChainConfig{EIP150Block: big.NewInt(20)}, + blockHeight: 9, + blockTimestamp: 90, + wantErr: nil, }, { - stored: TestChainConfig, - new: &ChainConfig{HomesteadBlock: nil}, - headHeight: 3, - headTimestamp: 30, + stored: TestChainConfig, + new: &ChainConfig{HomesteadBlock: nil}, + blockHeight: 3, + blockTimestamp: 30, wantErr: &ConfigCompatError{ What: "Homestead fork block", StoredConfig: big.NewInt(0), @@ -61,10 +61,10 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: TestChainConfig, - new: &ChainConfig{HomesteadBlock: big.NewInt(1)}, - headHeight: 3, - headTimestamp: 30, + stored: TestChainConfig, + new: &ChainConfig{HomesteadBlock: big.NewInt(1)}, + blockHeight: 3, + blockTimestamp: 30, wantErr: &ConfigCompatError{ What: "Homestead fork block", StoredConfig: big.NewInt(0), @@ -73,10 +73,10 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)}, - new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)}, - headHeight: 25, - headTimestamp: 250, + stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)}, + new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)}, + blockHeight: 25, + blockTimestamp: 250, wantErr: &ConfigCompatError{ What: "EIP150 fork block", StoredConfig: big.NewInt(10), @@ -85,17 +85,17 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)}, - new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)}, - headHeight: 40, - headTimestamp: 400, - wantErr: nil, + stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)}, + new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)}, + blockHeight: 40, + blockTimestamp: 400, + wantErr: nil, }, { - stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)}, - new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)}, - headHeight: 40, - headTimestamp: 400, + stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)}, + new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)}, + blockHeight: 40, + blockTimestamp: 400, wantErr: &ConfigCompatError{ What: "Petersburg fork block", StoredConfig: nil, @@ -104,10 +104,10 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: TestChainConfig, - new: TestApricotPhase4Config, - headHeight: 0, - headTimestamp: 0, + stored: TestChainConfig, + new: TestApricotPhase4Config, + blockHeight: 0, + blockTimestamp: 0, wantErr: &ConfigCompatError{ What: "ApricotPhase5 fork block timestamp", StoredConfig: big.NewInt(0), @@ -116,10 +116,10 @@ func TestCheckCompatible(t *testing.T) { }, }, { - stored: TestChainConfig, - new: TestApricotPhase4Config, - headHeight: 10, - headTimestamp: 100, + stored: TestChainConfig, + new: TestApricotPhase4Config, + blockHeight: 10, + blockTimestamp: 100, wantErr: &ConfigCompatError{ What: "ApricotPhase5 fork block timestamp", StoredConfig: big.NewInt(0), @@ -130,9 +130,9 @@ func TestCheckCompatible(t *testing.T) { } for _, test := range tests { - err := test.stored.CheckCompatible(test.new, test.headHeight, test.headTimestamp) + err := test.stored.CheckCompatible(test.new, test.blockHeight, test.blockTimestamp) if !reflect.DeepEqual(err, test.wantErr) { - t.Errorf("error mismatch:\nstored: %v\nnew: %v\nheadHeight: %v\nerr: %v\nwant: %v", test.stored, test.new, test.headHeight, err, test.wantErr) + t.Errorf("error mismatch:\nstored: %v\nnew: %v\nblockHeight: %v\nerr: %v\nwant: %v", test.stored, test.new, test.blockHeight, err, test.wantErr) } } } diff --git a/coreth/params/version.go b/coreth/params/version.go index 27e04794..ff5c122a 100644 --- a/coreth/params/version.go +++ b/coreth/params/version.go @@ -33,7 +33,7 @@ import ( const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 10 // Minor version component of the current release - VersionPatch = 21 // Patch version component of the current release + VersionPatch = 25 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string ) diff --git a/coreth/plugin/evm/atomic_backend.go b/coreth/plugin/evm/atomic_backend.go new file mode 100644 index 00000000..76a6ffb5 --- /dev/null +++ b/coreth/plugin/evm/atomic_backend.go @@ -0,0 +1,423 @@ +// (c) 2020-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "encoding/binary" + "fmt" + "time" + + "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/database/prefixdb" + "github.com/ava-labs/avalanchego/database/versiondb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/wrappers" + syncclient "github.com/ava-labs/coreth/sync/client" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +var _ AtomicBackend = &atomicBackend{} + +// AtomicBackend abstracts the verification and processing +// of atomic transactions +type AtomicBackend interface { + // InsertTxs calculates the root of the atomic trie that would + // result from applying [txs] to the atomic trie, starting at the state + // corresponding to previously verified block [parentHash]. + // If [blockHash] is provided, the modified atomic trie is pinned in memory + // and it's the caller's responsibility to call either Accept or Reject on + // the AtomicState which can be retreived from GetVerifiedAtomicState to commit the + // changes or abort them and free memory. + InsertTxs(blockHash common.Hash, blockHeight uint64, parentHash common.Hash, txs []*Tx) (common.Hash, error) + + // Returns an AtomicState corresponding to a block hash that has been inserted + // but not Accepted or Rejected yet. + GetVerifiedAtomicState(blockHash common.Hash) (AtomicState, error) + + // AtomicTrie returns the atomic trie managed by this backend. + AtomicTrie() AtomicTrie + + // ApplyToSharedMemory applies the atomic operations that have been indexed into the trie + // but not yet applied to shared memory for heights less than or equal to [lastAcceptedBlock]. + // This executes operations in the range [cursorHeight+1, lastAcceptedBlock]. + // The cursor is initially set by MarkApplyToSharedMemoryCursor to signal to the atomic trie + // the range of operations that were added to the trie without being executed on shared memory. + ApplyToSharedMemory(lastAcceptedBlock uint64) error + + // MarkApplyToSharedMemoryCursor marks the atomic trie as containing atomic ops that + // have not been executed on shared memory starting at [previousLastAcceptedHeight+1]. + // This is used when state sync syncs the atomic trie, such that the atomic operations + // from [previousLastAcceptedHeight+1] to the [lastAcceptedHeight] set by state sync + // will not have been executed on shared memory. + MarkApplyToSharedMemoryCursor(previousLastAcceptedHeight uint64) error + + // Syncer creates and returns a new Syncer object that can be used to sync the + // state of the atomic trie from peers + Syncer(client syncclient.LeafClient, targetRoot common.Hash, targetHeight uint64) (Syncer, error) + + // SetLastAccepted is used after state-sync to reset the last accepted block. + SetLastAccepted(lastAcceptedHash common.Hash) + + // IsBonus returns true if the block for atomicState is a bonus block + IsBonus(blockHeight uint64, blockHash common.Hash) bool +} + +// atomicBackend implements the AtomicBackend interface using +// the AtomicTrie, AtomicTxRepository, and the VM's shared memory. +type atomicBackend struct { + codec codec.Manager + bonusBlocks map[uint64]ids.ID // Map of height to blockID for blocks to skip indexing + db *versiondb.Database // Underlying database + metadataDB database.Database // Underlying database containing the atomic trie metadata + sharedMemory atomic.SharedMemory + + repo AtomicTxRepository + atomicTrie AtomicTrie + + lastAcceptedHash common.Hash + verifiedRoots map[common.Hash]AtomicState +} + +// NewAtomicBackend creates an AtomicBackend from the specified dependencies +func NewAtomicBackend( + db *versiondb.Database, sharedMemory atomic.SharedMemory, + bonusBlocks map[uint64]ids.ID, repo AtomicTxRepository, + lastAcceptedHeight uint64, lastAcceptedHash common.Hash, commitInterval uint64, +) (AtomicBackend, error) { + atomicTrieDB := prefixdb.New(atomicTrieDBPrefix, db) + metadataDB := prefixdb.New(atomicTrieMetaDBPrefix, db) + codec := repo.Codec() + + atomicTrie, err := newAtomicTrie(atomicTrieDB, metadataDB, codec, lastAcceptedHeight, commitInterval) + if err != nil { + return nil, err + } + atomicBackend := &atomicBackend{ + codec: codec, + db: db, + metadataDB: metadataDB, + sharedMemory: sharedMemory, + bonusBlocks: bonusBlocks, + repo: repo, + atomicTrie: atomicTrie, + lastAcceptedHash: lastAcceptedHash, + verifiedRoots: make(map[common.Hash]AtomicState), + } + + // We call ApplyToSharedMemory here to ensure that if the node was shut down in the middle + // of applying atomic operations from state sync, we finish the operation to ensure we never + // return an atomic trie that is out of sync with shared memory. + // In normal operation, the cursor is not set, such that this call will be a no-op. + if err := atomicBackend.ApplyToSharedMemory(lastAcceptedHeight); err != nil { + return nil, err + } + return atomicBackend, atomicBackend.initialize(lastAcceptedHeight) +} + +// initializes the atomic trie using the atomic repository height index. +// Iterating from the last committed height to the last height indexed +// in the atomic repository, making a single commit at the +// most recent height divisible by the commitInterval. +// Subsequent updates to this trie are made using the Index call as blocks are accepted. +// Note: this method assumes no atomic txs are applied at genesis. +func (a *atomicBackend) initialize(lastAcceptedHeight uint64) error { + start := time.Now() + + // track the last committed height and last committed root + lastCommittedRoot, lastCommittedHeight := a.atomicTrie.LastCommitted() + log.Info("initializing atomic trie", "lastCommittedHeight", lastCommittedHeight) + + // iterate by height, from [lastCommittedHeight+1] to [lastAcceptedBlockNumber] + height := lastCommittedHeight + iter := a.repo.IterateByHeight(lastCommittedHeight + 1) + defer iter.Release() + + heightsIndexed := 0 + lastUpdate := time.Now() + + // open the atomic trie at the last committed root + tr, err := a.atomicTrie.OpenTrie(lastCommittedRoot) + if err != nil { + return err + } + + for iter.Next() { + // Get the height and transactions for this iteration (from the key and value, respectively) + // iterate over the transactions, indexing them if the height is < commit height + // otherwise, add the atomic operations from the transaction to the uncommittedOpsMap + height = binary.BigEndian.Uint64(iter.Key()) + txs, err := ExtractAtomicTxs(iter.Value(), true, a.codec) + if err != nil { + return err + } + + // combine atomic operations from all transactions at this block height + combinedOps, err := mergeAtomicOps(txs) + if err != nil { + return err + } + + if _, found := a.bonusBlocks[height]; found { + // If [height] is a bonus block, do not index the atomic operations into the trie + continue + } + if err := a.atomicTrie.UpdateTrie(tr, height, combinedOps); err != nil { + return err + } + root, nodes, err := tr.Commit(false) + if err != nil { + return err + } + if err := a.atomicTrie.InsertTrie(nodes, root); err != nil { + return err + } + isCommit, err := a.atomicTrie.AcceptTrie(height, root) + if err != nil { + return err + } + if isCommit { + if err := a.db.Commit(); err != nil { + return err + } + } + + heightsIndexed++ + if time.Since(lastUpdate) > progressLogFrequency { + log.Info("imported entries into atomic trie", "heightsIndexed", heightsIndexed) + lastUpdate = time.Now() + } + } + if err := iter.Error(); err != nil { + return err + } + + // check if there are accepted blocks after the last block with accepted atomic txs. + if lastAcceptedHeight > height { + lastAcceptedRoot := a.atomicTrie.LastAcceptedRoot() + if err := a.atomicTrie.InsertTrie(nil, lastAcceptedRoot); err != nil { + return err + } + if _, err := a.atomicTrie.AcceptTrie(lastAcceptedHeight, lastAcceptedRoot); err != nil { + return err + } + } + + lastCommittedRoot, lastCommittedHeight = a.atomicTrie.LastCommitted() + log.Info( + "finished initializing atomic trie", + "lastAcceptedHeight", lastAcceptedHeight, + "lastAcceptedAtomicRoot", a.atomicTrie.LastAcceptedRoot(), + "heightsIndexed", heightsIndexed, + "lastCommittedRoot", lastCommittedRoot, + "lastCommittedHeight", lastCommittedHeight, + "time", time.Since(start), + ) + return nil +} + +// ApplyToSharedMemory applies the atomic operations that have been indexed into the trie +// but not yet applied to shared memory for heights less than or equal to [lastAcceptedBlock]. +// This executes operations in the range [cursorHeight+1, lastAcceptedBlock]. +// The cursor is initially set by MarkApplyToSharedMemoryCursor to signal to the atomic trie +// the range of operations that were added to the trie without being executed on shared memory. +func (a *atomicBackend) ApplyToSharedMemory(lastAcceptedBlock uint64) error { + sharedMemoryCursor, err := a.metadataDB.Get(appliedSharedMemoryCursorKey) + if err == database.ErrNotFound { + return nil + } else if err != nil { + return err + } + + lastCommittedRoot, _ := a.atomicTrie.LastCommitted() + log.Info("applying atomic operations to shared memory", "root", lastCommittedRoot, "lastAcceptedBlock", lastAcceptedBlock, "startHeight", binary.BigEndian.Uint64(sharedMemoryCursor[:wrappers.LongLen])) + + it, err := a.atomicTrie.Iterator(lastCommittedRoot, sharedMemoryCursor) + if err != nil { + return err + } + + var ( + lastUpdate = time.Now() + putRequests, removeRequests = 0, 0 + totalPutRequests, totalRemoveRequests = 0, 0 + ) + + // value of sharedMemoryCursor is either a uint64 signifying the + // height iteration should begin at or is a uint64+blockchainID + // specifying the last atomic operation that was applied to shared memory. + // To avoid applying the same operation twice, we call [it.Next()] in the + // latter case. + if len(sharedMemoryCursor) > wrappers.LongLen { + it.Next() + } + + batchOps := make(map[ids.ID]*atomic.Requests) + for it.Next() { + height := it.BlockNumber() + atomicOps := it.AtomicOps() + + if height > lastAcceptedBlock { + log.Warn("Found height above last accepted block while applying operations to shared memory", "height", height, "lastAcceptedBlock", lastAcceptedBlock) + break + } + + putRequests += len(atomicOps.PutRequests) + removeRequests += len(atomicOps.RemoveRequests) + totalPutRequests += len(atomicOps.PutRequests) + totalRemoveRequests += len(atomicOps.RemoveRequests) + if time.Since(lastUpdate) > progressLogFrequency { + log.Info("atomic trie iteration", "height", height, "puts", totalPutRequests, "removes", totalRemoveRequests) + lastUpdate = time.Now() + } + mergeAtomicOpsToMap(batchOps, it.BlockchainID(), atomicOps) + + if putRequests+removeRequests > sharedMemoryApplyBatchSize { + // Update the cursor to the key of the atomic operation being executed on shared memory. + // If the node shuts down in the middle of this function call, ApplyToSharedMemory will + // resume operation starting at the key immediately following [it.Key()]. + if err = a.metadataDB.Put(appliedSharedMemoryCursorKey, it.Key()); err != nil { + return err + } + batch, err := a.db.CommitBatch() + if err != nil { + return err + } + // calling [sharedMemory.Apply] updates the last applied pointer atomically with the shared memory operation. + if err = a.sharedMemory.Apply(batchOps, batch); err != nil { + return err + } + putRequests, removeRequests = 0, 0 + batchOps = make(map[ids.ID]*atomic.Requests) + } + } + if err := it.Error(); err != nil { + return err + } + + if err = a.metadataDB.Delete(appliedSharedMemoryCursorKey); err != nil { + return err + } + batch, err := a.db.CommitBatch() + if err != nil { + return err + } + if err = a.sharedMemory.Apply(batchOps, batch); err != nil { + return err + } + log.Info("finished applying atomic operations", "puts", totalPutRequests, "removes", totalRemoveRequests) + return nil +} + +// MarkApplyToSharedMemoryCursor marks the atomic trie as containing atomic ops that +// have not been executed on shared memory starting at [previousLastAcceptedHeight+1]. +// This is used when state sync syncs the atomic trie, such that the atomic operations +// from [previousLastAcceptedHeight+1] to the [lastAcceptedHeight] set by state sync +// will not have been executed on shared memory. +func (a *atomicBackend) MarkApplyToSharedMemoryCursor(previousLastAcceptedHeight uint64) error { + // Set the cursor to [previousLastAcceptedHeight+1] so that we begin the iteration at the + // first item that has not been applied to shared memory. + return database.PutUInt64(a.metadataDB, appliedSharedMemoryCursorKey, previousLastAcceptedHeight+1) +} + +// Syncer creates and returns a new Syncer object that can be used to sync the +// state of the atomic trie from peers +func (a *atomicBackend) Syncer(client syncclient.LeafClient, targetRoot common.Hash, targetHeight uint64) (Syncer, error) { + return newAtomicSyncer(client, a, targetRoot, targetHeight) +} + +func (a *atomicBackend) GetVerifiedAtomicState(blockHash common.Hash) (AtomicState, error) { + if state, ok := a.verifiedRoots[blockHash]; ok { + return state, nil + } + return nil, fmt.Errorf("cannot access atomic state for block %s", blockHash) +} + +// getAtomicRootAt returns the atomic trie root for a block that is either: +// - the last accepted block +// - a block that has been verified but not accepted or rejected yet. +// If [blockHash] is neither of the above, an error is returned. +func (a *atomicBackend) getAtomicRootAt(blockHash common.Hash) (common.Hash, error) { + // TODO: we can implement this in a few ways. + if blockHash == a.lastAcceptedHash { + return a.atomicTrie.LastAcceptedRoot(), nil + } + state, err := a.GetVerifiedAtomicState(blockHash) + if err != nil { + return common.Hash{}, err + } + return state.Root(), nil +} + +// SetLastAccepted is used after state-sync to update the last accepted block hash. +func (a *atomicBackend) SetLastAccepted(lastAcceptedHash common.Hash) { + a.lastAcceptedHash = lastAcceptedHash +} + +// InsertTxs calculates the root of the atomic trie that would +// result from applying [txs] to the atomic trie, starting at the state +// corresponding to previously verified block [parentHash]. +// If [blockHash] is provided, the modified atomic trie is pinned in memory +// and it's the caller's responsibility to call either Accept or Reject on +// the AtomicState which can be retreived from GetVerifiedAtomicState to commit the +// changes or abort them and free memory. +func (a *atomicBackend) InsertTxs(blockHash common.Hash, blockHeight uint64, parentHash common.Hash, txs []*Tx) (common.Hash, error) { + // access the atomic trie at the parent block + parentRoot, err := a.getAtomicRootAt(parentHash) + if err != nil { + return common.Hash{}, err + } + tr, err := a.atomicTrie.OpenTrie(parentRoot) + if err != nil { + return common.Hash{}, err + } + + // update the atomic trie + atomicOps, err := mergeAtomicOps(txs) + if err != nil { + return common.Hash{}, err + } + if err := a.atomicTrie.UpdateTrie(tr, blockHeight, atomicOps); err != nil { + return common.Hash{}, err + } + + // If block hash is not provided, we do not pin the atomic state in memory and can return early + if blockHash == (common.Hash{}) { + return tr.Hash(), nil + } + + // get the new root and pin the atomic trie changes in memory. + root, nodes, err := tr.Commit(false) + if err != nil { + return common.Hash{}, err + } + if err := a.atomicTrie.InsertTrie(nodes, root); err != nil { + return common.Hash{}, err + } + // track this block so further blocks can be inserted on top + // of this block + a.verifiedRoots[blockHash] = &atomicState{ + backend: a, + blockHash: blockHash, + blockHeight: blockHeight, + txs: txs, + atomicOps: atomicOps, + atomicRoot: root, + } + return root, nil +} + +// IsBonus returns true if the block for atomicState is a bonus block +func (a *atomicBackend) IsBonus(blockHeight uint64, blockHash common.Hash) bool { + if bonusID, found := a.bonusBlocks[blockHeight]; found { + return bonusID == ids.ID(blockHash) + } + return false +} + +func (a *atomicBackend) AtomicTrie() AtomicTrie { + return a.atomicTrie +} diff --git a/coreth/plugin/evm/atomic_state.go b/coreth/plugin/evm/atomic_state.go new file mode 100644 index 00000000..0cb7d2fe --- /dev/null +++ b/coreth/plugin/evm/atomic_state.go @@ -0,0 +1,96 @@ +// (c) 2020-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "fmt" + + "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +var _ AtomicState = &atomicState{} + +// AtomicState is an abstraction created through AtomicBackend +// and can be used to apply the VM's state change for atomic txs +// or reject them to free memory. +// The root of the atomic trie after applying the state change +// is accessible through this interface as well. +type AtomicState interface { + // Root of the atomic trie after applying the state change. + Root() common.Hash + // Accept applies the state change to VM's persistent storage + // Changes are persisted atomically along with the provided [commitBatch]. + Accept(commitBatch database.Batch) error + // Reject frees memory associated with the state change. + Reject() error +} + +// atomicState implements the AtomicState interface using +// a pointer to the atomicBackend. +type atomicState struct { + backend *atomicBackend + blockHash common.Hash + blockHeight uint64 + txs []*Tx + atomicOps map[ids.ID]*atomic.Requests + atomicRoot common.Hash +} + +func (a *atomicState) Root() common.Hash { + return a.atomicRoot +} + +// Accept applies the state change to VM's persistent storage. +func (a *atomicState) Accept(commitBatch database.Batch) error { + // Update the atomic tx repository. Note it is necessary to invoke + // the correct method taking bonus blocks into consideration. + if a.backend.IsBonus(a.blockHeight, a.blockHash) { + if err := a.backend.repo.WriteBonus(a.blockHeight, a.txs); err != nil { + return err + } + } else { + if err := a.backend.repo.Write(a.blockHeight, a.txs); err != nil { + return err + } + } + + // Accept the root of this atomic trie (will be persisted if at a commit interval) + if _, err := a.backend.atomicTrie.AcceptTrie(a.blockHeight, a.atomicRoot); err != nil { + return err + } + // Update the last accepted block to this block and remove it from + // the map tracking undecided blocks. + a.backend.lastAcceptedHash = a.blockHash + delete(a.backend.verifiedRoots, a.blockHash) + + // get changes from the atomic trie and repository in a batch + // to be committed atomically with [commitBatch] and shared memory. + atomicChangesBatch, err := a.backend.db.CommitBatch() + if err != nil { + return fmt.Errorf("could not create commit batch in atomicState accept: %w", err) + } + + // If this is a bonus block, write [commitBatch] without applying atomic ops + // to shared memory. + if a.backend.IsBonus(a.blockHeight, a.blockHash) { + log.Info("skipping atomic tx acceptance on bonus block", "block", a.blockHash) + return atomic.WriteAll(commitBatch, atomicChangesBatch) + } + + // Otherwise, atomically commit pending changes in the version db with + // atomic ops to shared memory. + return a.backend.sharedMemory.Apply(a.atomicOps, commitBatch, atomicChangesBatch) +} + +// Reject frees memory associated with the state change. +func (a *atomicState) Reject() error { + // Remove the block from the map of undecided blocks. + delete(a.backend.verifiedRoots, a.blockHash) + // Unpin the rejected atomic trie root from memory. + return a.backend.atomicTrie.RejectTrie(a.atomicRoot) +} diff --git a/coreth/plugin/evm/atomic_syncer.go b/coreth/plugin/evm/atomic_syncer.go index d794537c..5c70d551 100644 --- a/coreth/plugin/evm/atomic_syncer.go +++ b/coreth/plugin/evm/atomic_syncer.go @@ -9,10 +9,12 @@ import ( "encoding/binary" "fmt" + "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/coreth/plugin/evm/message" syncclient "github.com/ava-labs/coreth/sync/client" + "github.com/ava-labs/coreth/trie" "github.com/ethereum/go-ethereum/common" ) @@ -25,7 +27,9 @@ var ( // is responsible for orchestrating the sync while atomicSyncer is responsible for maintaining // the state of progress and writing the actual atomic trie to the trieDB. type atomicSyncer struct { - atomicTrie *atomicTrie + db *versiondb.Database + atomicTrie AtomicTrie + trie *trie.Trie // used to update the atomic trie targetRoot common.Hash targetHeight uint64 @@ -35,10 +39,6 @@ type atomicSyncer struct { // nextHeight is the height which key / values // are being inserted into [atomicTrie] for nextHeight uint64 - - // nextCommit is the next height at which the atomic trie - // should be committed. - nextCommit uint64 } // addZeros adds [common.HashLenth] zeros to [height] and returns the result as []byte @@ -49,21 +49,27 @@ func addZeroes(height uint64) []byte { return packer.Bytes } -func newAtomicSyncer(client syncclient.LeafClient, atomicTrie *atomicTrie, targetRoot common.Hash, targetHeight uint64) *atomicSyncer { - _, lastCommit := atomicTrie.LastCommitted() +func newAtomicSyncer(client syncclient.LeafClient, atomicBackend *atomicBackend, targetRoot common.Hash, targetHeight uint64) (*atomicSyncer, error) { + atomicTrie := atomicBackend.AtomicTrie() + lastCommittedRoot, lastCommit := atomicTrie.LastCommitted() + trie, err := atomicTrie.OpenTrie(lastCommittedRoot) + if err != nil { + return nil, err + } atomicSyncer := &atomicSyncer{ + db: atomicBackend.db, atomicTrie: atomicTrie, + trie: trie, targetRoot: targetRoot, targetHeight: targetHeight, - nextCommit: lastCommit + atomicTrie.commitHeightInterval, nextHeight: lastCommit + 1, } tasks := make(chan syncclient.LeafSyncTask, 1) tasks <- &atomicSyncerLeafTask{atomicSyncer: atomicSyncer} close(tasks) atomicSyncer.syncer = syncclient.NewCallbackLeafSyncer(client, tasks) - return atomicSyncer + return atomicSyncer, nil } // Start begins syncing the target atomic root. @@ -74,25 +80,41 @@ func (s *atomicSyncer) Start(ctx context.Context) error { // onLeafs is the callback for the leaf syncer, which will insert the key-value pairs into the trie. func (s *atomicSyncer) onLeafs(keys [][]byte, values [][]byte) error { + _, lastCommittedHeight := s.atomicTrie.LastCommitted() + lastHeight := lastCommittedHeight // track heights so we calculate roots after each height for i, key := range keys { if len(key) != atomicKeyLength { return fmt.Errorf("unexpected key len (%d) in atomic trie sync", len(key)) } // key = height + blockchainID height := binary.BigEndian.Uint64(key[:wrappers.LongLen]) - - // Commit the trie and update [nextCommit] if we are crossing a commit interval - for height > s.nextCommit { - if err := s.atomicTrie.commit(s.nextCommit); err != nil { + if height > lastHeight { + // If this key belongs to a new height, we commit + // the trie at the previous height before adding this key. + root, nodes, err := s.trie.Commit(false) + if err != nil { + return err + } + if err := s.atomicTrie.InsertTrie(nodes, root); err != nil { return err } - if err := s.atomicTrie.db.Commit(); err != nil { + // AcceptTrie commits the trieDB and returns [isCommit] as true + // if we have reached or crossed a commit interval. + isCommit, err := s.atomicTrie.AcceptTrie(lastHeight, root) + if err != nil { return err } - s.nextCommit += s.atomicTrie.commitHeightInterval + if isCommit { + // Flush pending changes to disk to preserve progress and + // free up memory if the trieDB was committed. + if err := s.db.Commit(); err != nil { + return err + } + } + lastHeight = height } - if err := s.atomicTrie.trie.TryUpdate(key, values[i]); err != nil { + if err := s.trie.TryUpdate(key, values[i]); err != nil { return err } } @@ -103,16 +125,22 @@ func (s *atomicSyncer) onLeafs(keys [][]byte, values [][]byte) error { // commit the trie to disk and perform the final checks that we synced the target root correctly. func (s *atomicSyncer) onFinish() error { // commit the trie on finish - if err := s.atomicTrie.commit(s.targetHeight); err != nil { + root, nodes, err := s.trie.Commit(false) + if err != nil { + return err + } + if err := s.atomicTrie.InsertTrie(nodes, root); err != nil { + return err + } + if _, err := s.atomicTrie.AcceptTrie(s.targetHeight, root); err != nil { return err } - if err := s.atomicTrie.db.Commit(); err != nil { + if err := s.db.Commit(); err != nil { return err } // the root of the trie should always match the targetRoot since we already verified the proofs, // here we check the root mainly for correctness of the atomicTrie's pointers and it should never fail. - root, _ := s.atomicTrie.LastCommitted() if s.targetRoot != root { return fmt.Errorf("synced root (%s) does not match expected (%s) for atomic trie ", root, s.targetRoot) } diff --git a/coreth/plugin/evm/atomic_syncer_test.go b/coreth/plugin/evm/atomic_syncer_test.go index 63e729d7..8cab412d 100644 --- a/coreth/plugin/evm/atomic_syncer_test.go +++ b/coreth/plugin/evm/atomic_syncer_test.go @@ -47,21 +47,24 @@ func testAtomicSyncer(t *testing.T, serverTrieDB *trie.Database, targetHeight ui ) clientDB := versiondb.New(memdb.New()) - - repo, err := NewAtomicTxRepository(clientDB, message.Codec, 0) + repo, err := NewAtomicTxRepository(clientDB, message.Codec, 0, nil, nil, nil) if err != nil { t.Fatal("could not initialize atomix tx repository", err) } - atomicTrie, err := newAtomicTrie(clientDB, testSharedMemory(), nil, repo, message.Codec, 0, commitInterval) + atomicBackend, err := NewAtomicBackend(clientDB, testSharedMemory(), nil, repo, 0, common.Hash{}, commitInterval) if err != nil { - t.Fatal("could not initialize atomic trie", err) + t.Fatal("could not initialize atomic backend", err) } + atomicTrie := atomicBackend.AtomicTrie() // For each checkpoint, replace the leafsIntercept to shut off the syncer at the correct point and force resume from the checkpoint's // next trie. for i, checkpoint := range checkpoints { // Create syncer targeting the current [syncTrie]. - syncer := newAtomicSyncer(mockClient, atomicTrie, targetRoot, targetHeight) + syncer, err := atomicBackend.Syncer(mockClient, targetRoot, targetHeight) + if err != nil { + t.Fatal(err) + } mockClient.GetLeafsIntercept = func(_ message.LeafsRequest, leafsResponse message.LeafsResponse) (message.LeafsResponse, error) { // If this request exceeds the desired number of leaves, intercept the request with an error if numLeaves+len(leafsResponse.Keys) > checkpoint.leafCutoff { @@ -85,7 +88,10 @@ func testAtomicSyncer(t *testing.T, serverTrieDB *trie.Database, targetHeight ui } // Create syncer targeting the current [targetRoot]. - syncer := newAtomicSyncer(mockClient, atomicTrie, targetRoot, targetHeight) + syncer, err := atomicBackend.Syncer(mockClient, targetRoot, targetHeight) + if err != nil { + t.Fatal(err) + } // Update intercept to only count the leaves mockClient.GetLeafsIntercept = func(_ message.LeafsRequest, leafsResponse message.LeafsResponse) (message.LeafsResponse, error) { @@ -107,7 +113,7 @@ func testAtomicSyncer(t *testing.T, serverTrieDB *trie.Database, targetHeight ui trie.AssertTrieConsistency(t, targetRoot, serverTrieDB, clientTrieDB, nil) // check all commit heights are created - for height := atomicTrie.commitHeightInterval; height <= targetHeight; height += atomicTrie.commitHeightInterval { + for height := uint64(commitInterval); height <= targetHeight; height += commitInterval { root, err := atomicTrie.Root(height) assert.NoError(t, err) assert.NotZero(t, root) diff --git a/coreth/plugin/evm/atomic_trie.go b/coreth/plugin/evm/atomic_trie.go index 1b6e35bf..88ef7811 100644 --- a/coreth/plugin/evm/atomic_trie.go +++ b/coreth/plugin/evm/atomic_trie.go @@ -11,24 +11,25 @@ import ( "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/database" - "github.com/ava-labs/avalanchego/database/prefixdb" - "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/types" - syncclient "github.com/ava-labs/coreth/sync/client" + "github.com/ava-labs/coreth/ethdb" "github.com/ava-labs/coreth/trie" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) const ( - trieCommitSizeCap = 10 * units.MiB - progressLogUpdate = 30 * time.Second + progressLogFrequency = 30 * time.Second atomicKeyLength = wrappers.LongLen + common.HashLength sharedMemoryApplyBatchSize = 10_000 // specifies the number of atomic operations to batch progress updates + + atomicTrieTipBufferSize = 1 // No need to support a buffer of previously accepted tries for the atomic trie + atomicTrieMemoryCap = 64 * units.MiB ) var ( @@ -43,9 +44,12 @@ var ( // are the atomic operations applied to shared memory while processing the block accepted // at the corresponding height. type AtomicTrie interface { - // Index indexes the given atomicOps at the specified block height - // Atomic trie is committed if the block height is multiple of commit interval - Index(height uint64, atomicOps map[ids.ID]*atomic.Requests) error + // OpenTrie returns a modifiable instance of the atomic trie backed by trieDB + // opened at hash. + OpenTrie(hash common.Hash) (*trie.Trie, error) + + // UpdateTrie updates [tr] to inlude atomicOps for height. + UpdateTrie(tr *trie.Trie, height uint64, atomicOps map[ids.ID]*atomic.Requests) error // Iterator returns an AtomicTrieIterator to iterate the trie at the given // root hash starting at [cursor]. @@ -62,23 +66,22 @@ type AtomicTrie interface { // common.Hash{} instead Root(height uint64) (common.Hash, error) - // ApplyToSharedMemory applies the atomic operations that have been indexed into the trie - // but not yet applied to shared memory for heights less than or equal to [lastAcceptedBlock]. - // This executes operations in the range [cursorHeight+1, lastAcceptedBlock]. - // The cursor is initially set by MarkApplyToSharedMemoryCursor to signal to the atomic trie - // the range of operations that were added to the trie without being executed on shared memory. - ApplyToSharedMemory(lastAcceptedBlock uint64) error - - // MarkApplyToSharedMemoryCursor marks the atomic trie as containing atomic ops that - // have not been executed on shared memory starting at [previousLastAcceptedHeight+1]. - // This is used when state sync syncs the atomic trie, such that the atomic operations - // from [previousLastAcceptedHeight+1] to the [lastAcceptedHeight] set by state sync - // will not have been executed on shared memory. - MarkApplyToSharedMemoryCursor(previousLastAcceptedHeight uint64) error - - // Syncer creates and returns a new Syncer object that can be used to sync the - // state of the atomic trie from peers - Syncer(client syncclient.LeafClient, targetRoot common.Hash, targetHeight uint64) Syncer + // LastAcceptedRoot returns the most recent accepted root of the atomic trie, + // or the root it was initialized to if no new tries were accepted yet. + LastAcceptedRoot() common.Hash + + // InsertTrie updates the trieDB with the provided node set and adds a reference + // to root in the trieDB. Once InsertTrie is called, it is expected either + // AcceptTrie or RejectTrie be called for the same root. + InsertTrie(nodes *trie.NodeSet, root common.Hash) error + + // AcceptTrie marks root as the last accepted atomic trie root, and + // commits the trie to persistent storage if height is divisible by + // the commit interval. Returns true if the trie was committed. + AcceptTrie(height uint64, root common.Hash) (bool, error) + + // RejectTrie dereferences root from the trieDB, freeing memory. + RejectTrie(root common.Hash) error } // AtomicTrieIterator is a stateful iterator that iterates the leafs of an AtomicTrie @@ -106,47 +109,32 @@ type AtomicTrieIterator interface { } // atomicTrie implements the AtomicTrie interface -// using the eth trie.Trie implementation type atomicTrie struct { - commitHeightInterval uint64 // commit interval, same as commitHeightInterval by default - db *versiondb.Database // Underlying database - bonusBlocks map[uint64]ids.ID // Map of height to blockID for blocks to skip indexing - metadataDB database.Database // Underlying database containing the atomic trie metadata - atomicTrieDB database.Database // Underlying database containing the atomic trie - trieDB *trie.Database // Trie database - trie *trie.Trie // Atomic trie.Trie mapping key (height+blockchainID) and value (codec serialized atomic.Requests) - repo AtomicTxRepository - lastCommittedHash common.Hash // trie root hash of the most recent commit - lastCommittedHeight uint64 // index height of the most recent commit - codec codec.Manager - log log.Logger // struct logger - sharedMemory atomic.SharedMemory -} - -// NewAtomicTrie returns a new instance of a atomicTrie with the default commitHeightInterval. -// Initializes the trie before returning it. -// If the cursor set by MarkApplyToSharedMemoryCursor exists, the atomic operations are applied synchronously -// during initialization (blocks until ApplyToSharedMemory completes). -func NewAtomicTrie( - db *versiondb.Database, sharedMemory atomic.SharedMemory, - bonusBlocks map[uint64]ids.ID, repo AtomicTxRepository, codec codec.Manager, lastAcceptedHeight uint64, commitHeightInterval uint64, -) (AtomicTrie, error) { - return newAtomicTrie(db, sharedMemory, bonusBlocks, repo, codec, lastAcceptedHeight, commitHeightInterval) + commitInterval uint64 // commit interval, same as commitHeightInterval by default + metadataDB database.Database // Underlying database containing the atomic trie metadata + trieDB *trie.Database // Trie database + lastCommittedRoot common.Hash // trie root of the most recent commit + lastCommittedHeight uint64 // index height of the most recent commit + lastAcceptedRoot common.Hash // most recent trie root passed to accept trie or the root of the atomic trie on intialization. + codec codec.Manager + memoryCap common.StorageSize + tipBuffer *core.BoundedBuffer } // newAtomicTrie returns a new instance of a atomicTrie with a configurable commitHeightInterval, used in testing. // Initializes the trie before returning it. func newAtomicTrie( - db *versiondb.Database, sharedMemory atomic.SharedMemory, - bonusBlocks map[uint64]ids.ID, repo AtomicTxRepository, codec codec.Manager, - lastAcceptedHeight uint64, commitHeightInterval uint64, + atomicTrieDB database.Database, metadataDB database.Database, + codec codec.Manager, lastAcceptedHeight uint64, commitHeightInterval uint64, ) (*atomicTrie, error) { - atomicTrieDB := prefixdb.New(atomicTrieDBPrefix, db) - metadataDB := prefixdb.New(atomicTrieMetaDBPrefix, db) root, height, err := lastCommittedRootIfExists(metadataDB) if err != nil { return nil, err } + // initialize to EmptyRootHash if there is no committed root. + if root == (common.Hash{}) { + root = types.EmptyRootHash + } // If the last committed height is above the last accepted height, then we fall back to // the last commit below the last accepted height. if height > lastAcceptedHeight { @@ -157,42 +145,28 @@ func newAtomicTrie( } } - triedb := trie.NewDatabaseWithConfig( + trieDB := trie.NewDatabaseWithConfig( Database{atomicTrieDB}, &trie.Config{ - Cache: 64, // Allocate 64MB of memory for clean cache - Preimages: false, // Keys are not hashed, so there is no need for preimages + Cache: 64, // Allocate 64MB of memory for clean cache }, ) - t, err := trie.New(common.Hash{}, root, triedb) - if err != nil { - return nil, err - } - - atomicTrie := &atomicTrie{ - commitHeightInterval: commitHeightInterval, - db: db, - bonusBlocks: bonusBlocks, - atomicTrieDB: atomicTrieDB, - metadataDB: metadataDB, - trieDB: triedb, - trie: t, - repo: repo, - codec: codec, - lastCommittedHash: root, - lastCommittedHeight: height, - log: log.New("c", "atomicTrie"), - sharedMemory: sharedMemory, - } - // We call ApplyToSharedMemory here to ensure that if the node was shut down in the middle - // of applying atomic operations from state sync, we finish the operation to ensure we never - // return an atomic trie that is out of sync with shared memory. - // In normal operation, the cursor is not set, such that this call will be a no-op. - if err := atomicTrie.ApplyToSharedMemory(lastAcceptedHeight); err != nil { - return nil, err - } - return atomicTrie, atomicTrie.initialize(lastAcceptedHeight) + return &atomicTrie{ + commitInterval: commitHeightInterval, + metadataDB: metadataDB, + trieDB: trieDB, + codec: codec, + lastCommittedRoot: root, + lastCommittedHeight: height, + tipBuffer: core.NewBoundedBuffer(atomicTrieTipBufferSize, trieDB.Dereference), + memoryCap: atomicTrieMemoryCap, + // Initialize lastAcceptedRoot to the last committed root. + // If there were further blocks processed (ahead of the commit interval), + // AtomicBackend will call InsertTrie/AcceptTrie on atomic ops + // for those blocks. + lastAcceptedRoot: root, + }, nil } // lastCommittedRootIfExists returns the last committed trie root and height if it exists @@ -223,203 +197,20 @@ func nearestCommitHeight(blockNumber uint64, commitInterval uint64) uint64 { return blockNumber - (blockNumber % commitInterval) } -// initializes the atomic trie using the atomic repository height index. -// Iterating from the last indexed height to lastAcceptedBlockNumber, making a single commit at the -// most recent height divisible by the commitInterval. -// Subsequent updates to this trie are made using the Index call as blocks are accepted. -// Note: this method assumes no atomic txs are applied at genesis. -func (a *atomicTrie) initialize(lastAcceptedBlockNumber uint64) error { - start := time.Now() - a.log.Info("initializing atomic trie", "lastAcceptedBlockNumber", lastAcceptedBlockNumber) - // finalCommitHeight is the highest block that can be committed i.e. is divisible by b.commitHeightInterval - // Txs from heights greater than commitHeight are to be included in the trie corresponding to the block at - // finalCommitHeight+b.commitHeightInterval, which has not been accepted yet. - finalCommitHeight := nearestCommitHeight(lastAcceptedBlockNumber, a.commitHeightInterval) - uncommittedOpsMap := make(map[uint64]map[ids.ID]*atomic.Requests, lastAcceptedBlockNumber-finalCommitHeight) - - // iterate by height, from [a.lastCommittedHeight+1] to [lastAcceptedBlockNumber] - iter := a.repo.IterateByHeight(a.lastCommittedHeight + 1) - defer iter.Release() - - preCommitBlockIndexed := 0 - postCommitTxIndexed := 0 - lastUpdate := time.Now() - - // keep track of the latest generated trie's root and height. - lastHash := common.Hash{} - lastHeight := a.lastCommittedHeight - for iter.Next() { - // Get the height and transactions for this iteration (from the key and value, respectively) - // iterate over the transactions, indexing them if the height is < commit height - // otherwise, add the atomic operations from the transaction to the uncommittedOpsMap - height := binary.BigEndian.Uint64(iter.Key()) - txs, err := ExtractAtomicTxs(iter.Value(), true, a.codec) - if err != nil { - return err - } - - // combine atomic operations from all transactions at this block height - combinedOps, err := mergeAtomicOps(txs) - if err != nil { - return err - } - - if _, skipBonusBlock := a.bonusBlocks[height]; skipBonusBlock { - // If [height] is a bonus block, do not index the atomic operations into the trie - } else if height > finalCommitHeight { - // if height is greater than commit height, add it to the map so that we can write it later - // this is to ensure we have all the data before the commit height so that we can commit the - // trie - uncommittedOpsMap[height] = combinedOps - } else { - if err := a.updateTrie(height, combinedOps); err != nil { - return err - } - preCommitBlockIndexed++ - } - - if time.Since(lastUpdate) > progressLogUpdate { - a.log.Info("imported entries into atomic trie pre-commit", "heightsIndexed", preCommitBlockIndexed) - lastUpdate = time.Now() - } - - // if height has reached or skipped over the next commit interval, - // keep track of progress and keep commit size under commitSizeCap - commitHeight := nearestCommitHeight(height, a.commitHeightInterval) - if lastHeight < commitHeight { - hash, _, err := a.trie.Commit(nil, false) - if err != nil { - return err - } - // Dereference lashHash to avoid writing more intermediary - // trie nodes than needed to disk, while keeping the commit - // size under commitSizeCap (approximately). - // Check [lastHash != hash] here to avoid dereferencing the - // trie root in case there were no atomic txs since the - // last commit. - if (lastHash != common.Hash{} && lastHash != hash) { - a.trieDB.Dereference(lastHash) - } - storage, _ := a.trieDB.Size() - if storage > trieCommitSizeCap { - a.log.Info("committing atomic trie progress", "storage", storage) - a.commit(commitHeight) - // Flush any remaining changes that have not been committed yet in the versiondb. - if err := a.db.Commit(); err != nil { - return err - } - } - lastHash = hash - lastHeight = commitHeight - } - } - if err := iter.Error(); err != nil { - return err - } - - // Note: we should never create a commit at the genesis block (should not contain any atomic txs) - if lastAcceptedBlockNumber == 0 { - return nil - } - // now that all heights less than [finalCommitHeight] have been processed - // commit the trie. If [a.lastCommittedHeight] is already the same as - // [finalCommitHeight] (or higher, which can occur if we resume a state sync) - // we skip this commit. - if finalCommitHeight > a.lastCommittedHeight { - if err := a.commit(finalCommitHeight); err != nil { - return err - } - // Flush any remaining changes that have not been committed yet in the versiondb. - if err := a.db.Commit(); err != nil { - return err - } - } - - // process uncommitted ops for heights > finalCommitHeight - for height, ops := range uncommittedOpsMap { - if err := a.updateTrie(height, ops); err != nil { - return fmt.Errorf("failed to update trie at height %d: %w", height, err) - } - - postCommitTxIndexed++ - if time.Since(lastUpdate) > progressLogUpdate { - a.log.Info("imported entries into atomic trie post-commit", "entriesIndexed", postCommitTxIndexed) - lastUpdate = time.Now() - } - } - - a.log.Info( - "finished initializing atomic trie", - "lastAcceptedBlockNumber", lastAcceptedBlockNumber, - "preCommitEntriesIndexed", preCommitBlockIndexed, - "postCommitEntriesIndexed", postCommitTxIndexed, - "lastCommittedHash", a.lastCommittedHash, - "lastCommittedHeight", a.lastCommittedHeight, - "time", time.Since(start), - ) - return nil -} - -// Index updates the trie with entries in atomicOps -// height must be greater than lastCommittedHeight and less than (lastCommittedHeight+commitInterval) -// This function updates the following: -// - heightBytes => trie root hash (if the trie was committed) -// - lastCommittedBlock => height (if the trie was committed) -func (a *atomicTrie) Index(height uint64, atomicOps map[ids.ID]*atomic.Requests) error { - if err := a.validateIndexHeight(height); err != nil { - return err - } - - if err := a.updateTrie(height, atomicOps); err != nil { - return err - } - - if height%a.commitHeightInterval == 0 { - return a.commit(height) - } - - return nil -} - -// validateIndexHeight returns an error if [height] is not currently valid to be indexed. -func (a *atomicTrie) validateIndexHeight(height uint64) error { - // Do not allow a height that we have already passed to be indexed - if height < a.lastCommittedHeight { - return fmt.Errorf("height %d must be after last committed height %d", height, a.lastCommittedHeight) - } - - // Do not allow a height that is more than a commit interval ahead - // of the current index - nextCommitHeight := a.lastCommittedHeight + a.commitHeightInterval - if height > nextCommitHeight { - return fmt.Errorf("height %d not within the next commit height %d", height, nextCommitHeight) - } - - return nil +func (a *atomicTrie) OpenTrie(root common.Hash) (*trie.Trie, error) { + return trie.New(common.Hash{}, root, a.trieDB) } -// commit calls commit on the trie to generate a root, commits the underlying trieDB, and updates the -// metadata pointers. -// assumes that the caller is aware of the commit rules i.e. the height being within commitInterval. -// returns the trie root from the commit -func (a *atomicTrie) commit(height uint64) error { - hash, _, err := a.trie.Commit(nil, false) - if err != nil { +// commit calls commit on the underlying trieDB and updates metadata pointers. +func (a *atomicTrie) commit(height uint64, root common.Hash) error { + if err := a.trieDB.Commit(root, false, nil); err != nil { return err } - - a.log.Info("committed atomic trie", "hash", hash.String(), "height", height) - if err := a.trieDB.Commit(hash, false, nil); err != nil { - return err - } - - if err := a.updateLastCommitted(hash, height); err != nil { - return err - } - return nil + log.Info("committed atomic trie", "root", root.String(), "height", height) + return a.updateLastCommitted(root, height) } -func (a *atomicTrie) updateTrie(height uint64, atomicOps map[ids.ID]*atomic.Requests) error { +func (a *atomicTrie) UpdateTrie(trie *trie.Trie, height uint64, atomicOps map[ids.ID]*atomic.Requests) error { for blockchainID, requests := range atomicOps { valueBytes, err := a.codec.Marshal(codecVersion, requests) if err != nil { @@ -432,7 +223,7 @@ func (a *atomicTrie) updateTrie(height uint64, atomicOps map[ids.ID]*atomic.Requ keyPacker := wrappers.Packer{Bytes: make([]byte, atomicKeyLength)} keyPacker.PackLong(height) keyPacker.PackFixedBytes(blockchainID[:]) - if err := a.trie.TryUpdate(keyPacker.Bytes, valueBytes); err != nil { + if err := trie.TryUpdate(keyPacker.Bytes, valueBytes); err != nil { return err } } @@ -442,7 +233,7 @@ func (a *atomicTrie) updateTrie(height uint64, atomicOps map[ids.ID]*atomic.Requ // LastCommitted returns the last committed trie hash and last committed height func (a *atomicTrie) LastCommitted() (common.Hash, uint64) { - return a.lastCommittedHash, a.lastCommittedHeight + return a.lastCommittedRoot, a.lastCommittedHeight } // updateLastCommitted adds [height] -> [root] to the index and marks it as the last committed @@ -461,7 +252,7 @@ func (a *atomicTrie) updateLastCommitted(root common.Hash, height uint64) error return err } - a.lastCommittedHash = root + a.lastCommittedRoot = root a.lastCommittedHeight = height return nil } @@ -512,108 +303,64 @@ func getRoot(metadataDB database.Database, height uint64) (common.Hash, error) { return common.BytesToHash(hash), nil } -// ApplyToSharedMemory applies the atomic operations that have been indexed into the trie -// but not yet applied to shared memory for heights less than or equal to [lastAcceptedBlock]. -// This executes operations in the range [cursorHeight+1, lastAcceptedBlock]. -// The cursor is initially set by MarkApplyToSharedMemoryCursor to signal to the atomic trie -// the range of operations that were added to the trie without being executed on shared memory. -func (a *atomicTrie) ApplyToSharedMemory(lastAcceptedBlock uint64) error { - sharedMemoryCursor, err := a.metadataDB.Get(appliedSharedMemoryCursorKey) - if err == database.ErrNotFound { - return nil - } else if err != nil { - return err - } +func (a *atomicTrie) LastAcceptedRoot() common.Hash { + return a.lastAcceptedRoot +} - log.Info("applying atomic operations to shared memory", "root", a.lastCommittedHash, "lastAcceptedBlock", lastAcceptedBlock, "startHeight", binary.BigEndian.Uint64(sharedMemoryCursor[:wrappers.LongLen])) +func (a *atomicTrie) InsertTrie(nodes *trie.NodeSet, root common.Hash) error { + if nodes != nil { + if err := a.trieDB.Update(trie.NewWithNodeSet(nodes)); err != nil { + return err + } + } + a.trieDB.Reference(root, common.Hash{}) - it, err := a.Iterator(a.lastCommittedHash, sharedMemoryCursor) - if err != nil { - return err + // The use of [Cap] in [insertTrie] prevents exceeding the configured memory + // limit (and OOM) in case there is a large backlog of processing (unaccepted) blocks. + if nodeSize, _ := a.trieDB.Size(); nodeSize <= a.memoryCap { + return nil } - lastUpdate := time.Now() - putRequests, removeRequests := 0, 0 - totalPutRequests, totalRemoveRequests := 0, 0 - - // value of sharedMemoryCursor is either a uint64 signifying the - // height iteration should begin at or is a uint64+blockchainID - // specifying the last atomic operation that was applied to shared memory. - // To avoid applying the same operation twice, we call [it.Next()] in the - // latter case. - if len(sharedMemoryCursor) > wrappers.LongLen { - it.Next() + if err := a.trieDB.Cap(a.memoryCap - ethdb.IdealBatchSize); err != nil { + return fmt.Errorf("failed to cap atomic trie for root %s: %w", root, err) } - batchOps := make(map[ids.ID]*atomic.Requests) - for it.Next() { - height := it.BlockNumber() - atomicOps := it.AtomicOps() - - if height > lastAcceptedBlock { - log.Warn("Found height above last accepted block while applying operations to shared memory", "height", height, "lastAcceptedBlock", lastAcceptedBlock) - break - } + return nil +} - putRequests += len(atomicOps.PutRequests) - removeRequests += len(atomicOps.RemoveRequests) - totalPutRequests += len(atomicOps.PutRequests) - totalRemoveRequests += len(atomicOps.RemoveRequests) - if time.Since(lastUpdate) > 10*time.Second { - log.Info("atomic trie iteration", "height", height, "puts", totalPutRequests, "removes", totalRemoveRequests) - lastUpdate = time.Now() - } - mergeAtomicOpsToMap(batchOps, it.BlockchainID(), atomicOps) - - if putRequests+removeRequests > sharedMemoryApplyBatchSize { - // Update the cursor to the key of the atomic operation being executed on shared memory. - // If the node shuts down in the middle of this function call, ApplyToSharedMemory will - // resume operation starting at the key immediately following [it.Key()]. - if err = a.metadataDB.Put(appliedSharedMemoryCursorKey, it.Key()); err != nil { - return err - } - batch, err := a.db.CommitBatch() - if err != nil { - return err - } - // calling [sharedMemory.Apply] updates the last applied pointer atomically with the shared memory operation. - if err = a.sharedMemory.Apply(batchOps, batch); err != nil { - return err - } - putRequests, removeRequests = 0, 0 - batchOps = make(map[ids.ID]*atomic.Requests) +// AcceptTrie commits the triedb at [root] if needed and returns true if a commit +// was performed. +func (a *atomicTrie) AcceptTrie(height uint64, root common.Hash) (bool, error) { + // Check whether we have crossed over a commitHeight. + // If so, make a commit with the last accepted root. + hasCommitted := false + commitHeight := nearestCommitHeight(height, a.commitInterval) + for commitHeight > a.lastCommittedHeight && height > commitHeight { + nextCommitHeight := a.lastCommittedHeight + a.commitInterval + if err := a.commit(nextCommitHeight, a.lastAcceptedRoot); err != nil { + return false, err } - } - if err := it.Error(); err != nil { - return err + hasCommitted = true } - if err = a.metadataDB.Delete(appliedSharedMemoryCursorKey); err != nil { - return err - } - batch, err := a.db.CommitBatch() - if err != nil { - return err - } - if err = a.sharedMemory.Apply(batchOps, batch); err != nil { - return err + // Attempt to dereference roots at least [tipBufferSize] old + // + // Note: It is safe to dereference roots that have been committed to disk + // (they are no-ops). + a.tipBuffer.Insert(root) + + // Commit this root if we have reached the [commitInterval]. + if commitHeight == height { + if err := a.commit(height, root); err != nil { + return false, err + } + hasCommitted = true } - log.Info("finished applying atomic operations", "puts", totalPutRequests, "removes", totalRemoveRequests) - return nil -} -// MarkApplyToSharedMemoryCursor marks the atomic trie as containing atomic ops that -// have not been executed on shared memory starting at [previousLastAcceptedHeight+1]. -// This is used when state sync syncs the atomic trie, such that the atomic operations -// from [previousLastAcceptedHeight+1] to the [lastAcceptedHeight] set by state sync -// will not have been executed on shared memory. -func (a *atomicTrie) MarkApplyToSharedMemoryCursor(previousLastAcceptedHeight uint64) error { - // Set the cursor to [previousLastAcceptedHeight+1] so that we begin the iteration at the - // first item that has not been applied to shared memory. - return database.PutUInt64(a.metadataDB, appliedSharedMemoryCursorKey, previousLastAcceptedHeight+1) + a.lastAcceptedRoot = root + return hasCommitted, nil } -// Syncer creates and returns a new Syncer object that can be used to sync the -// state of the atomic trie from peers -func (a *atomicTrie) Syncer(client syncclient.LeafClient, targetRoot common.Hash, targetHeight uint64) Syncer { - return newAtomicSyncer(client, a, targetRoot, targetHeight) +func (a *atomicTrie) RejectTrie(root common.Hash) error { + a.trieDB.Dereference(root) + return nil } diff --git a/coreth/plugin/evm/atomic_trie_iterator_test.go b/coreth/plugin/evm/atomic_trie_iterator_test.go index 31b91bbf..944d2c47 100644 --- a/coreth/plugin/evm/atomic_trie_iterator_test.go +++ b/coreth/plugin/evm/atomic_trie_iterator_test.go @@ -13,6 +13,7 @@ import ( "github.com/ava-labs/avalanchego/utils" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func testSharedMemory() atomic.SharedMemory { @@ -24,7 +25,7 @@ func TestIteratorCanIterate(t *testing.T) { lastAcceptedHeight := uint64(1000) db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight) + repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight, nil, nil, nil) assert.NoError(t, err) // create state with multiple transactions @@ -35,8 +36,9 @@ func TestIteratorCanIterate(t *testing.T) { // create an atomic trie // on create it will initialize all the transactions from the above atomic repository - atomicTrie1, err := newAtomicTrie(db, testSharedMemory(), nil, repo, codec, lastAcceptedHeight, 100) + atomicBackend, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, lastAcceptedHeight, common.Hash{}, 100) assert.NoError(t, err) + atomicTrie1 := atomicBackend.AtomicTrie() lastCommittedHash1, lastCommittedHeight1 := atomicTrie1.LastCommitted() assert.NoError(t, err) @@ -47,8 +49,9 @@ func TestIteratorCanIterate(t *testing.T) { // iterate on a new atomic trie to make sure there is no resident state affecting the data and the // iterator - atomicTrie2, err := newAtomicTrie(db, testSharedMemory(), nil, repo, codec, lastAcceptedHeight, 100) + atomicBackend2, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, lastAcceptedHeight, common.Hash{}, 100) assert.NoError(t, err) + atomicTrie2 := atomicBackend2.AtomicTrie() lastCommittedHash2, lastCommittedHeight2 := atomicTrie2.LastCommitted() assert.NoError(t, err) assert.NotEqual(t, common.Hash{}, lastCommittedHash2) @@ -58,11 +61,12 @@ func TestIteratorCanIterate(t *testing.T) { } func TestIteratorHandlesInvalidData(t *testing.T) { + require := require.New(t) lastAcceptedHeight := uint64(1000) db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight) - assert.NoError(t, err) + repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight, nil, nil, nil) + require.NoError(err) // create state with multiple transactions // since each test transaction generates random ID for blockchainID we should get @@ -72,24 +76,36 @@ func TestIteratorHandlesInvalidData(t *testing.T) { // create an atomic trie // on create it will initialize all the transactions from the above atomic repository - atomicTrie, err := newAtomicTrie(db, testSharedMemory(), nil, repo, codec, lastAcceptedHeight, 100) - assert.NoError(t, err) + commitInterval := uint64(100) + atomicBackend, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, lastAcceptedHeight, common.Hash{}, commitInterval) + require.NoError(err) + atomicTrie := atomicBackend.AtomicTrie() lastCommittedHash, lastCommittedHeight := atomicTrie.LastCommitted() - assert.NoError(t, err) - assert.NotEqual(t, common.Hash{}, lastCommittedHash) - assert.EqualValues(t, 1000, lastCommittedHeight) + require.NoError(err) + require.NotEqual(common.Hash{}, lastCommittedHash) + require.EqualValues(1000, lastCommittedHeight) verifyOperations(t, atomicTrie, codec, lastCommittedHash, 1, 1000, operationsMap) // Add a random key-value pair to the atomic trie in order to test that the iterator correctly // handles an error when it runs into an unexpected key-value pair in the trie. - assert.NoError(t, atomicTrie.trie.TryUpdate(utils.RandomBytes(50), utils.RandomBytes(50))) - assert.NoError(t, atomicTrie.commit(lastCommittedHeight+1)) + atomicTrieSnapshot, err := atomicTrie.OpenTrie(lastCommittedHash) + require.NoError(err) + require.NoError(atomicTrieSnapshot.TryUpdate(utils.RandomBytes(50), utils.RandomBytes(50))) + + nextRoot, nodes, err := atomicTrieSnapshot.Commit(false) + require.NoError(err) + err = atomicTrie.InsertTrie(nodes, nextRoot) + require.NoError(err) + isCommit, err := atomicTrie.AcceptTrie(lastCommittedHeight+commitInterval, nextRoot) + require.NoError(err) + require.True(isCommit) + corruptedHash, _ := atomicTrie.LastCommitted() iter, err := atomicTrie.Iterator(corruptedHash, nil) - assert.NoError(t, err) + require.NoError(err) for iter.Next() { } - assert.Error(t, iter.Error()) + require.Error(iter.Error()) } diff --git a/coreth/plugin/evm/atomic_trie_test.go b/coreth/plugin/evm/atomic_trie_test.go index 8bee4605..05e94cc7 100644 --- a/coreth/plugin/evm/atomic_trie_test.go +++ b/coreth/plugin/evm/atomic_trie_test.go @@ -33,6 +33,28 @@ func (tx *Tx) mustAtomicOps() map[ids.ID]*atomic.Requests { return map[ids.ID]*atomic.Requests{id: reqs} } +// indexAtomicTxs updates [tr] with entries in [atomicOps] at height by creating +// a new snapshot, calculating a new root, and calling InsertTrie followed +// by AcceptTrie on the new root. +func indexAtomicTxs(tr AtomicTrie, height uint64, atomicOps map[ids.ID]*atomic.Requests) error { + snapshot, err := tr.OpenTrie(tr.LastAcceptedRoot()) + if err != nil { + return err + } + if err := tr.UpdateTrie(snapshot, height, atomicOps); err != nil { + return err + } + root, nodes, err := snapshot.Commit(false) + if err != nil { + return err + } + if err := tr.InsertTrie(nodes, root); err != nil { + return err + } + _, err = tr.AcceptTrie(height, root) + return err +} + func TestNearestCommitHeight(t *testing.T) { type test struct { height, commitInterval, expectedCommitHeight uint64 @@ -117,7 +139,7 @@ func TestAtomicTrieInitialize(t *testing.T) { t.Run(name, func(t *testing.T) { db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, test.lastAcceptedHeight) + repo, err := NewAtomicTxRepository(db, codec, test.lastAcceptedHeight, nil, nil, nil) if err != nil { t.Fatal(err) } @@ -125,10 +147,12 @@ func TestAtomicTrieInitialize(t *testing.T) { writeTxs(t, repo, 1, test.lastAcceptedHeight+1, test.numTxsPerBlock, nil, operationsMap) // Construct the atomic trie for the first time - atomicTrie1, err := newAtomicTrie(db, testSharedMemory(), nil, repo, codec, test.lastAcceptedHeight, test.commitInterval) + atomicBackend1, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, test.lastAcceptedHeight, common.Hash{}, test.commitInterval) if err != nil { t.Fatal(err) } + atomicTrie1 := atomicBackend1.AtomicTrie() + rootHash1, commitHeight1 := atomicTrie1.LastCommitted() assert.EqualValues(t, test.expectedCommitHeight, commitHeight1) if test.expectedCommitHeight != 0 { @@ -138,14 +162,26 @@ func TestAtomicTrieInitialize(t *testing.T) { // Verify the operations up to the expected commit height verifyOperations(t, atomicTrie1, codec, rootHash1, 1, test.expectedCommitHeight, operationsMap) - // Construct the atomic trie a second time and ensure that it produces the same hash - atomicTrie2, err := newAtomicTrie(versiondb.New(memdb.New()), testSharedMemory(), nil, repo, codec, test.lastAcceptedHeight, test.commitInterval) + // Construct the atomic trie again (on the same database) and ensure the last accepted root is correct. + atomicBackend2, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, test.lastAcceptedHeight, common.Hash{}, test.commitInterval) + if err != nil { + t.Fatal(err) + } + atomicTrie2 := atomicBackend2.AtomicTrie() + assert.Equal(t, atomicTrie1.LastAcceptedRoot(), atomicTrie2.LastAcceptedRoot()) + + // Construct the atomic trie again (on an empty database) and ensure that it produces the same hash. + atomicBackend3, err := NewAtomicBackend( + versiondb.New(memdb.New()), testSharedMemory(), nil, repo, test.lastAcceptedHeight, common.Hash{}, test.commitInterval, + ) if err != nil { t.Fatal(err) } - rootHash2, commitHeight2 := atomicTrie2.LastCommitted() - assert.EqualValues(t, commitHeight1, commitHeight2) - assert.EqualValues(t, rootHash1, rootHash2) + atomicTrie3 := atomicBackend3.AtomicTrie() + + rootHash3, commitHeight3 := atomicTrie3.LastCommitted() + assert.EqualValues(t, commitHeight1, commitHeight3) + assert.EqualValues(t, rootHash1, rootHash3) // We now index additional operations up the next commit interval in order to confirm that nothing // during the initialization phase will cause an invalid root when indexing continues. @@ -160,12 +196,11 @@ func TestAtomicTrieInitialize(t *testing.T) { if err != nil { t.Fatal(err) } - if err := atomicTrie1.Index(i, atomicOps); err != nil { + if err := indexAtomicTxs(atomicTrie1, i, atomicOps); err != nil { t.Fatal(err) } operationsMap[i] = atomicOps } - updatedRoot, updatedLastCommitHeight := atomicTrie1.LastCommitted() assert.EqualValues(t, nextCommitHeight, updatedLastCommitHeight) assert.NotEqual(t, common.Hash{}, updatedRoot) @@ -174,13 +209,17 @@ func TestAtomicTrieInitialize(t *testing.T) { verifyOperations(t, atomicTrie1, codec, updatedRoot, 1, updatedLastCommitHeight, operationsMap) // Generate a new atomic trie to compare the root against. - atomicTrie3, err := newAtomicTrie(versiondb.New(memdb.New()), testSharedMemory(), nil, repo, codec, nextCommitHeight, test.commitInterval) + atomicBackend4, err := NewAtomicBackend( + versiondb.New(memdb.New()), testSharedMemory(), nil, repo, nextCommitHeight, common.Hash{}, test.commitInterval, + ) if err != nil { t.Fatal(err) } - rootHash3, commitHeight3 := atomicTrie3.LastCommitted() - assert.EqualValues(t, rootHash3, updatedRoot) - assert.EqualValues(t, updatedLastCommitHeight, commitHeight3) + atomicTrie4 := atomicBackend4.AtomicTrie() + + rootHash4, commitHeight4 := atomicTrie4.LastCommitted() + assert.EqualValues(t, updatedRoot, rootHash4) + assert.EqualValues(t, updatedLastCommitHeight, commitHeight4) }) } } @@ -189,14 +228,15 @@ func TestIndexerInitializesOnlyOnce(t *testing.T) { lastAcceptedHeight := uint64(25) db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight) + repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight, nil, nil, nil) assert.NoError(t, err) operationsMap := make(map[uint64]map[ids.ID]*atomic.Requests) writeTxs(t, repo, 1, lastAcceptedHeight+1, constTxsPerHeight(2), nil, operationsMap) // Initialize atomic repository - atomicTrie, err := newAtomicTrie(db, testSharedMemory(), nil, repo, codec, lastAcceptedHeight, 10 /*commitHeightInterval*/) + atomicBackend, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, lastAcceptedHeight, common.Hash{}, 10 /* commitInterval*/) assert.NoError(t, err) + atomicTrie := atomicBackend.AtomicTrie() hash, height := atomicTrie.LastCommitted() assert.NotEqual(t, common.Hash{}, hash) @@ -210,26 +250,30 @@ func TestIndexerInitializesOnlyOnce(t *testing.T) { assert.NoError(t, err) // Re-initialize the atomic trie - atomicTrie, err = newAtomicTrie(db, testSharedMemory(), nil, repo, codec, lastAcceptedHeight, 10 /*commitHeightInterval*/) + atomicBackend, err = NewAtomicBackend(db, testSharedMemory(), nil, repo, lastAcceptedHeight, common.Hash{}, 10 /* commitInterval */) assert.NoError(t, err) + atomicTrie = atomicBackend.AtomicTrie() newHash, newHeight := atomicTrie.LastCommitted() assert.Equal(t, height, newHeight, "height should not have changed") assert.Equal(t, hash, newHash, "hash should be the same") } -func newTestAtomicTrieIndexer(t *testing.T) AtomicTrie { +func newTestAtomicTrie(t *testing.T) AtomicTrie { db := versiondb.New(memdb.New()) - repo, err := NewAtomicTxRepository(db, testTxCodec(), 0) - assert.NoError(t, err) - indexer, err := newAtomicTrie(db, testSharedMemory(), nil, repo, testTxCodec(), 0, testCommitInterval) - assert.NoError(t, err) - assert.NotNil(t, indexer) - return indexer + repo, err := NewAtomicTxRepository(db, testTxCodec(), 0, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + atomicBackend, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, 0, common.Hash{}, testCommitInterval) + if err != nil { + t.Fatal(err) + } + return atomicBackend.AtomicTrie() } func TestIndexerWriteAndRead(t *testing.T) { - atomicTrie := newTestAtomicTrieIndexer(t) + atomicTrie := newTestAtomicTrie(t) blockRootMap := make(map[uint64]common.Hash) lastCommittedBlockHeight := uint64(0) @@ -238,7 +282,7 @@ func TestIndexerWriteAndRead(t *testing.T) { // process 305 blocks so that we get three commits (100, 200, 300) for height := uint64(1); height <= testCommitInterval*3+5; /*=305*/ height++ { atomicRequests := testDataImportTx().mustAtomicOps() - err := atomicTrie.Index(height, atomicRequests) + err := indexAtomicTxs(atomicTrie, height, atomicRequests) assert.NoError(t, err) if height%testCommitInterval == 0 { lastCommittedBlockHash, lastCommittedBlockHeight = atomicTrie.LastCommitted() @@ -261,22 +305,11 @@ func TestIndexerWriteAndRead(t *testing.T) { assert.NoError(t, err) assert.Equal(t, hash, root) } - - // Ensure that Index refuses to accept blocks older than the last committed height - err := atomicTrie.Index(10, testDataExportTx().mustAtomicOps()) - assert.Error(t, err) - assert.Equal(t, "height 10 must be after last committed height 300", err.Error()) - - // Ensure Index does not accept blocks beyond the next commit interval - nextCommitHeight := lastCommittedBlockHeight + testCommitInterval + 1 // =301 - err = atomicTrie.Index(nextCommitHeight, testDataExportTx().mustAtomicOps()) - assert.Error(t, err) - assert.Equal(t, "height 401 not within the next commit height 400", err.Error()) } func TestAtomicOpsAreNotTxOrderDependent(t *testing.T) { - atomicTrie1 := newTestAtomicTrieIndexer(t) - atomicTrie2 := newTestAtomicTrieIndexer(t) + atomicTrie1 := newTestAtomicTrie(t) + atomicTrie2 := newTestAtomicTrie(t) for height := uint64(0); height <= testCommitInterval; /*=205*/ height++ { tx1 := testDataImportTx() @@ -286,9 +319,9 @@ func TestAtomicOpsAreNotTxOrderDependent(t *testing.T) { atomicRequests2, err := mergeAtomicOps([]*Tx{tx2, tx1}) assert.NoError(t, err) - err = atomicTrie1.Index(height, atomicRequests1) + err = indexAtomicTxs(atomicTrie1, height, atomicRequests1) assert.NoError(t, err) - err = atomicTrie2.Index(height, atomicRequests2) + err = indexAtomicTxs(atomicTrie2, height, atomicRequests2) assert.NoError(t, err) } root1, height1 := atomicTrie1.LastCommitted() @@ -306,7 +339,7 @@ func TestAtomicTrieSkipsBonusBlocks(t *testing.T) { expectedCommitHeight := uint64(100) db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight) + repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight, nil, nil, nil) if err != nil { t.Fatal(err) } @@ -319,10 +352,12 @@ func TestAtomicTrieSkipsBonusBlocks(t *testing.T) { 14: {}, } // Construct the atomic trie for the first time - atomicTrie, err := newAtomicTrie(db, testSharedMemory(), bonusBlocks, repo, codec, lastAcceptedHeight, commitInterval) + atomicBackend, err := NewAtomicBackend(db, testSharedMemory(), bonusBlocks, repo, lastAcceptedHeight, common.Hash{}, commitInterval) if err != nil { t.Fatal(err) } + atomicTrie := atomicBackend.AtomicTrie() + rootHash, commitHeight := atomicTrie.LastCommitted() assert.EqualValues(t, expectedCommitHeight, commitHeight) assert.NotEqual(t, common.Hash{}, rootHash) @@ -342,10 +377,10 @@ func TestIndexingNilShouldNotImpactTrie(t *testing.T) { } // without nils - a1 := newTestAtomicTrieIndexer(t) + a1 := newTestAtomicTrie(t) for i := uint64(0); i <= testCommitInterval; i++ { if i%2 == 0 { - if err := a1.Index(i, ops[i]); err != nil { + if err := indexAtomicTxs(a1, i, ops[i]); err != nil { t.Fatal(err) } } else { @@ -358,14 +393,14 @@ func TestIndexingNilShouldNotImpactTrie(t *testing.T) { assert.Equal(t, uint64(testCommitInterval), height1) // with nils - a2 := newTestAtomicTrieIndexer(t) + a2 := newTestAtomicTrie(t) for i := uint64(0); i <= testCommitInterval; i++ { if i%2 == 0 { - if err := a2.Index(i, ops[i]); err != nil { + if err := indexAtomicTxs(a2, i, ops[i]); err != nil { t.Fatal(err) } } else { - if err := a2.Index(i, nil); err != nil { + if err := indexAtomicTxs(a2, i, nil); err != nil { t.Fatal(err) } } @@ -450,7 +485,7 @@ func newSharedMemories(atomicMemory *atomic.Memory, thisChainID, peerChainID ids func TestApplyToSharedMemory(t *testing.T) { type test struct { commitInterval, lastAcceptedHeight uint64 - setMarker func(*atomicTrie) error + setMarker func(*atomicBackend) error expectOpsApplied func(height uint64) bool } @@ -458,13 +493,13 @@ func TestApplyToSharedMemory(t *testing.T) { "marker is set to height": { commitInterval: 10, lastAcceptedHeight: 25, - setMarker: func(a *atomicTrie) error { return a.MarkApplyToSharedMemoryCursor(10) }, + setMarker: func(a *atomicBackend) error { return a.MarkApplyToSharedMemoryCursor(10) }, expectOpsApplied: func(height uint64) bool { return height > 10 && height <= 20 }, }, "marker is set to height + blockchain ID": { commitInterval: 10, lastAcceptedHeight: 25, - setMarker: func(a *atomicTrie) error { + setMarker: func(a *atomicBackend) error { cursor := make([]byte, wrappers.LongLen+len(blockChainID[:])) binary.BigEndian.PutUint64(cursor, 10) copy(cursor[wrappers.LongLen:], blockChainID[:]) @@ -475,14 +510,14 @@ func TestApplyToSharedMemory(t *testing.T) { "marker not set": { commitInterval: 10, lastAcceptedHeight: 25, - setMarker: func(*atomicTrie) error { return nil }, + setMarker: func(*atomicBackend) error { return nil }, expectOpsApplied: func(uint64) bool { return false }, }, } { t.Run(name, func(t *testing.T) { db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, test.lastAcceptedHeight) + repo, err := NewAtomicTxRepository(db, codec, test.lastAcceptedHeight, nil, nil, nil) assert.NoError(t, err) operationsMap := make(map[uint64]map[ids.ID]*atomic.Requests) writeTxs(t, repo, 1, test.lastAcceptedHeight+1, constTxsPerHeight(2), nil, operationsMap) @@ -490,8 +525,9 @@ func TestApplyToSharedMemory(t *testing.T) { // Initialize atomic repository m := atomic.NewMemory(db) sharedMemories := newSharedMemories(m, testCChainID, blockChainID) - atomicTrie, err := newAtomicTrie(db, sharedMemories.thisChain, nil, repo, codec, test.lastAcceptedHeight, test.commitInterval) + backend, err := NewAtomicBackend(db, sharedMemories.thisChain, nil, repo, test.lastAcceptedHeight, common.Hash{}, test.commitInterval) assert.NoError(t, err) + atomicTrie := backend.AtomicTrie().(*atomicTrie) hash, height := atomicTrie.LastCommitted() assert.NotEqual(t, common.Hash{}, hash) @@ -504,9 +540,9 @@ func TestApplyToSharedMemory(t *testing.T) { } } - assert.NoError(t, test.setMarker(atomicTrie)) + assert.NoError(t, test.setMarker(backend.(*atomicBackend))) assert.NoError(t, db.Commit()) - assert.NoError(t, atomicTrie.ApplyToSharedMemory(test.lastAcceptedHeight)) + assert.NoError(t, backend.ApplyToSharedMemory(test.lastAcceptedHeight)) // assert ops were applied as expected for height, ops := range operationsMap { @@ -522,7 +558,9 @@ func TestApplyToSharedMemory(t *testing.T) { assert.NoError(t, err) assert.False(t, hasMarker) // reinitialize the atomic trie - atomicTrie, err = newAtomicTrie(db, sharedMemories.thisChain, nil, repo, codec, test.lastAcceptedHeight, test.commitInterval) + backend, err = NewAtomicBackend( + db, sharedMemories.thisChain, nil, repo, test.lastAcceptedHeight, common.Hash{}, test.commitInterval, + ) assert.NoError(t, err) // no further changes should have occurred in shared memory // assert they are as they were prior to reinitializing @@ -550,18 +588,22 @@ func BenchmarkAtomicTrieInit(b *testing.B) { lastAcceptedHeight := uint64(25000) // add 25000 * 3 = 75000 transactions - repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight) + repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight, nil, nil, nil) assert.NoError(b, err) writeTxs(b, repo, 1, lastAcceptedHeight, constTxsPerHeight(3), nil, operationsMap) - var atomicTrie AtomicTrie - var hash common.Hash - var height uint64 + var ( + atomicTrie AtomicTrie + hash common.Hash + height uint64 + ) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - atomicTrie, err = newAtomicTrie(db, testSharedMemory(), nil, repo, codec, lastAcceptedHeight, 5000) + sharedMemory := testSharedMemory() + atomicBackend, err := NewAtomicBackend(db, sharedMemory, nil, repo, lastAcceptedHeight, common.Hash{}, 5000) assert.NoError(b, err) + atomicTrie = atomicBackend.AtomicTrie() hash, height = atomicTrie.LastCommitted() assert.Equal(b, lastAcceptedHeight, height) @@ -581,19 +623,15 @@ func BenchmarkAtomicTrieIterate(b *testing.B) { lastAcceptedHeight := uint64(25_000) // add 25000 * 3 = 75000 transactions - repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight) + repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight, nil, nil, nil) assert.NoError(b, err) writeTxs(b, repo, 1, lastAcceptedHeight, constTxsPerHeight(3), nil, operationsMap) - var ( - atomicTrie AtomicTrie - hash common.Hash - height uint64 - ) - atomicTrie, err = newAtomicTrie(db, testSharedMemory(), nil, repo, codec, lastAcceptedHeight, 5000) + atomicBackend, err := NewAtomicBackend(db, testSharedMemory(), nil, repo, lastAcceptedHeight, common.Hash{}, 5000) assert.NoError(b, err) + atomicTrie := atomicBackend.AtomicTrie() - hash, height = atomicTrie.LastCommitted() + hash, height := atomicTrie.LastCommitted() assert.Equal(b, lastAcceptedHeight, height) assert.NotEqual(b, common.Hash{}, hash) @@ -659,34 +697,34 @@ func BenchmarkApplyToSharedMemory(b *testing.B) { func benchmarkApplyToSharedMemory(b *testing.B, disk database.Database, blocks uint64) { db := versiondb.New(disk) codec := testTxCodec() + sharedMemory := testSharedMemory() lastAcceptedHeight := blocks - repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight) - assert.NoError(b, err) - - var atomicTrie *atomicTrie - var hash common.Hash - var height uint64 - atomicTrie, err = newAtomicTrie(db, testSharedMemory(), nil, repo, codec, 0, 5000) + repo, err := NewAtomicTxRepository(db, codec, lastAcceptedHeight, nil, nil, nil) assert.NoError(b, err) + backend, err := NewAtomicBackend(db, sharedMemory, nil, repo, 0, common.Hash{}, 5000) + if err != nil { + b.Fatal(err) + } + trie := backend.AtomicTrie() for height := uint64(1); height <= lastAcceptedHeight; height++ { txs := newTestTxs(constTxsPerHeight(3)(height)) ops, err := mergeAtomicOps(txs) assert.NoError(b, err) - assert.NoError(b, atomicTrie.Index(height, ops)) + assert.NoError(b, indexAtomicTxs(trie, height, ops)) } - hash, height = atomicTrie.LastCommitted() + hash, height := trie.LastCommitted() assert.Equal(b, lastAcceptedHeight, height) assert.NotEqual(b, common.Hash{}, hash) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - atomicTrie.sharedMemory = testSharedMemory() - assert.NoError(b, atomicTrie.MarkApplyToSharedMemoryCursor(0)) - assert.NoError(b, atomicTrie.db.Commit()) - assert.NoError(b, atomicTrie.ApplyToSharedMemory(lastAcceptedHeight)) + backend.(*atomicBackend).sharedMemory = testSharedMemory() + assert.NoError(b, backend.MarkApplyToSharedMemoryCursor(0)) + assert.NoError(b, db.Commit()) + assert.NoError(b, backend.ApplyToSharedMemory(lastAcceptedHeight)) } } diff --git a/coreth/plugin/evm/atomic_tx_repository.go b/coreth/plugin/evm/atomic_tx_repository.go index ae5e0b21..6bb863d5 100644 --- a/coreth/plugin/evm/atomic_tx_repository.go +++ b/coreth/plugin/evm/atomic_tx_repository.go @@ -5,6 +5,7 @@ package evm import ( "encoding/binary" + "errors" "fmt" "sort" "time" @@ -42,10 +43,8 @@ type AtomicTxRepository interface { Write(height uint64, txs []*Tx) error WriteBonus(height uint64, txs []*Tx) error - IterateByHeight(uint64) database.Iterator - - IsBonusBlocksRepaired() (bool, error) - MarkBonusBlocksRepaired(repairedEntries uint64) error + IterateByHeight(start uint64) database.Iterator + Codec() codec.Manager } // atomicTxRepository is a prefixdb implementation of the AtomicTxRepository interface @@ -67,7 +66,11 @@ type atomicTxRepository struct { codec codec.Manager } -func NewAtomicTxRepository(db *versiondb.Database, codec codec.Manager, lastAcceptedHeight uint64) (*atomicTxRepository, error) { +func NewAtomicTxRepository( + db *versiondb.Database, codec codec.Manager, lastAcceptedHeight uint64, + bonusBlocks map[uint64]ids.ID, canonicalBlocks []uint64, + getAtomicTxFromBlockByHeight func(height uint64) (*Tx, error), +) (*atomicTxRepository, error) { repo := &atomicTxRepository{ acceptedAtomicTxDB: prefixdb.New(atomicTxIDDBPrefix, db), acceptedAtomicTxByHeightDB: prefixdb.New(atomicHeightTxDBPrefix, db), @@ -75,7 +78,17 @@ func NewAtomicTxRepository(db *versiondb.Database, codec codec.Manager, lastAcce codec: codec, db: db, } - return repo, repo.initializeHeightIndex(lastAcceptedHeight) + if err := repo.initializeHeightIndex(lastAcceptedHeight); err != nil { + return nil, err + } + + // TODO: remove post banff as all network participants will have applied the repair script. + repairHeights := getAtomicRepositoryRepairHeights(bonusBlocks, canonicalBlocks) + if err := repo.RepairForBonusBlocks(repairHeights, getAtomicTxFromBlockByHeight); err != nil { + return nil, fmt.Errorf("failed to repair atomic repository: %w", err) + } + + return repo, nil } // initializeHeightIndex initializes the atomic repository and takes care of any required migration from the previous database @@ -357,12 +370,88 @@ func (a *atomicTxRepository) IterateByHeight(height uint64) database.Iterator { return a.acceptedAtomicTxByHeightDB.NewIteratorWithStart(heightBytes) } -func (a *atomicTxRepository) IsBonusBlocksRepaired() (bool, error) { +func (a *atomicTxRepository) Codec() codec.Manager { + return a.codec +} + +func (a *atomicTxRepository) isBonusBlocksRepaired() (bool, error) { return a.atomicRepoMetadataDB.Has(bonusBlocksRepairedKey) } -func (a *atomicTxRepository) MarkBonusBlocksRepaired(repairedEntries uint64) error { +func (a *atomicTxRepository) markBonusBlocksRepaired(repairedEntries uint64) error { val := make([]byte, wrappers.LongLen) binary.BigEndian.PutUint64(val, repairedEntries) return a.atomicRepoMetadataDB.Put(bonusBlocksRepairedKey, val) } + +// RepairForBonusBlocks ensures that atomic txs that were processed on more than one block +// (canonical block + a number of bonus blocks) are indexed to the first height they were +// processed on (canonical block). [sortedHeights] should include all canonical block and +// bonus block heights in ascending order, and will only be passed as non-empty on mainnet. +func (a *atomicTxRepository) RepairForBonusBlocks( + sortedHeights []uint64, getAtomicTxFromBlockByHeight func(height uint64) (*Tx, error), +) error { + done, err := a.isBonusBlocksRepaired() + if err != nil { + return err + } + if done { + return nil + } + repairedEntries := uint64(0) + seenTxs := make(map[ids.ID][]uint64) + for _, height := range sortedHeights { + // get atomic tx from block + tx, err := getAtomicTxFromBlockByHeight(height) + if err != nil { + return err + } + if tx == nil { + continue + } + + // get the tx by txID and update it, the first time we encounter + // a given [txID], overwrite the previous [txID] => [height] + // mapping. This provides a canonical mapping across nodes. + heights, seen := seenTxs[tx.ID()] + _, foundHeight, err := a.GetByTxID(tx.ID()) + if err != nil && !errors.Is(err, database.ErrNotFound) { + return err + } + if !seen { + if err := a.Write(height, []*Tx{tx}); err != nil { + return err + } + } else { + if err := a.WriteBonus(height, []*Tx{tx}); err != nil { + return err + } + } + if foundHeight != height && !seen { + repairedEntries++ + } + seenTxs[tx.ID()] = append(heights, height) + } + if err := a.markBonusBlocksRepaired(repairedEntries); err != nil { + return err + } + log.Info("atomic tx repository RepairForBonusBlocks complete", "repairedEntries", repairedEntries) + return a.db.Commit() +} + +// getAtomicRepositoryRepairHeights returns a slice containing heights from bonus blocks and +// canonical blocks sorted by height. +func getAtomicRepositoryRepairHeights(bonusBlocks map[uint64]ids.ID, canonicalBlocks []uint64) []uint64 { + repairHeights := make([]uint64, 0, len(bonusBlocks)+len(canonicalBlocks)) + for height := range bonusBlocks { + repairHeights = append(repairHeights, height) + } + for _, height := range canonicalBlocks { + // avoid appending duplicates + if _, exists := bonusBlocks[height]; !exists { + repairHeights = append(repairHeights, height) + } + } + sort.Slice(repairHeights, func(i, j int) bool { return repairHeights[i] < repairHeights[j] }) + return repairHeights +} diff --git a/coreth/plugin/evm/atomic_tx_repository_test.go b/coreth/plugin/evm/atomic_tx_repository_test.go index eaf3fce5..8bdeaacd 100644 --- a/coreth/plugin/evm/atomic_tx_repository_test.go +++ b/coreth/plugin/evm/atomic_tx_repository_test.go @@ -121,6 +121,8 @@ func verifyTxs(t testing.TB, repo AtomicTxRepository, txMap map[uint64][]*Tx) { // verifyOperations creates an iterator over the atomicTrie at [rootHash] and verifies that the all of the operations in the trie in the interval [from, to] are identical to // the atomic operations contained in [operationsMap] on the same interval. func verifyOperations(t testing.TB, atomicTrie AtomicTrie, codec codec.Manager, rootHash common.Hash, from, to uint64, operationsMap map[uint64]map[ids.ID]*atomic.Requests) { + t.Helper() + // Start the iterator at [from] fromBytes := make([]byte, wrappers.LongLen) binary.BigEndian.PutUint64(fromBytes, from) @@ -186,7 +188,7 @@ func verifyOperations(t testing.TB, atomicTrie AtomicTrie, codec codec.Manager, func TestAtomicRepositoryReadWriteSingleTx(t *testing.T) { db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, 0) + repo, err := NewAtomicTxRepository(db, codec, 0, nil, nil, nil) if err != nil { t.Fatal(err) } @@ -199,7 +201,7 @@ func TestAtomicRepositoryReadWriteSingleTx(t *testing.T) { func TestAtomicRepositoryReadWriteMultipleTxs(t *testing.T) { db := versiondb.New(memdb.New()) codec := testTxCodec() - repo, err := NewAtomicTxRepository(db, codec, 0) + repo, err := NewAtomicTxRepository(db, codec, 0, nil, nil, nil) if err != nil { t.Fatal(err) } @@ -222,7 +224,7 @@ func TestAtomicRepositoryPreAP5Migration(t *testing.T) { // Ensure the atomic repository can correctly migrate the transactions // from the old accepted atomic tx DB to add the height index. - repo, err := NewAtomicTxRepository(db, codec, 100) + repo, err := NewAtomicTxRepository(db, codec, 100, nil, nil, nil) if err != nil { t.Fatal(err) } @@ -248,7 +250,7 @@ func TestAtomicRepositoryPostAP5Migration(t *testing.T) { // Ensure the atomic repository can correctly migrate the transactions // from the old accepted atomic tx DB to add the height index. - repo, err := NewAtomicTxRepository(db, codec, 200) + repo, err := NewAtomicTxRepository(db, codec, 200, nil, nil, nil) if err != nil { t.Fatal(err) } @@ -270,7 +272,7 @@ func benchAtomicRepositoryIndex10_000(b *testing.B, maxHeight uint64, txsPerHeig if err := db.Commit(); err != nil { b.Fatal(err) } - repo, err := NewAtomicTxRepository(db, codec, maxHeight) + repo, err := NewAtomicTxRepository(db, codec, maxHeight, nil, nil, nil) if err != nil { b.Fatal(err) } @@ -292,15 +294,18 @@ func BenchmarkAtomicRepositoryIndex_10kBlocks_10Tx(b *testing.B) { func TestRepairAtomicRepositoryForBonusBlockTxs(t *testing.T) { db := versiondb.New(memdb.New()) - atomicTxRepository, err := NewAtomicTxRepository(db, testTxCodec(), 0) + atomicTxRepository, err := NewAtomicTxRepository(db, testTxCodec(), 0, nil, nil, nil) if err != nil { t.Fatal(err) } - // check completion flag is not set - done, err := atomicTxRepository.IsBonusBlocksRepaired() + // check completion flag is set + done, err := atomicTxRepository.isBonusBlocksRepaired() assert.NoError(t, err) - assert.False(t, done) + assert.True(t, done) + + // delete the key so we can simulate an unrepaired repository + atomicTxRepository.atomicRepoMetadataDB.Delete(bonusBlocksRepairedKey) tx := newTestTx() // write the same tx to 3 heights. @@ -315,9 +320,7 @@ func TestRepairAtomicRepositoryForBonusBlockTxs(t *testing.T) { assert.Equal(t, bonus2, foundHeight) allHeights := []uint64{canonical, bonus1, bonus2} - if err := repairAtomicRepositoryForBonusBlockTxs( - atomicTxRepository, - db, + if err := atomicTxRepository.RepairForBonusBlocks( allHeights, func(height uint64) (*Tx, error) { if height == 10 || height == 20 || height == 30 { @@ -345,7 +348,7 @@ func TestRepairAtomicRepositoryForBonusBlockTxs(t *testing.T) { } // check completion flag is set - done, err = atomicTxRepository.IsBonusBlocksRepaired() + done, err = atomicTxRepository.isBonusBlocksRepaired() assert.NoError(t, err) assert.True(t, done) } diff --git a/coreth/plugin/evm/block.go b/coreth/plugin/evm/block.go index dc36b8b8..e65776ba 100644 --- a/coreth/plugin/evm/block.go +++ b/coreth/plugin/evm/block.go @@ -13,19 +13,16 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ava-labs/coreth/core/types" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/choices" ) var ( - bonusBlocks = ids.Set{} bonusBlockMainnetHeights = make(map[uint64]ids.ID) // first height that processed a TX included on a // bonus block is the canonical height for that TX. - canonicalBonusBlocks = []uint64{ + canonicalBlockMainnetHeights = []uint64{ 102928, 103035, 103038, 103114, 103193, 103234, 103338, 103444, 103480, 103491, 103513, 103533, 103535, 103538, 103541, @@ -100,7 +97,6 @@ func init() { if err != nil { panic(err) } - bonusBlocks.Add(blkID) bonusBlockMainnetHeights[height] = blkID } } @@ -150,53 +146,23 @@ func (b *Block) Accept() error { return fmt.Errorf("failed to put %s as the last accepted block: %w", b.ID(), err) } - if len(b.atomicTxs) == 0 { - if err := b.vm.atomicTrie.Index(b.Height(), nil); err != nil { - return err - } - return vm.db.Commit() - } - - batchChainsAndInputs, err := mergeAtomicOps(b.atomicTxs) - if err != nil { - return err - } for _, tx := range b.atomicTxs { // Remove the accepted transaction from the mempool vm.mempool.RemoveTx(tx) } - isBonus := bonusBlocks.Contains(b.id) - if err := b.indexAtomics(vm, b.Height(), b.atomicTxs, batchChainsAndInputs, isBonus); err != nil { + // Update VM state for atomic txs in this block. This includes updating the + // atomic tx repo, atomic trie, and shared memory. + atomicState, err := b.vm.atomicBackend.GetVerifiedAtomicState(common.Hash(b.ID())) + if err != nil { + // should never occur since [b] must be verified before calling Accept return err } - // If [b] is a bonus block, then we commit the database without applying the requests from - // the atmoic transactions to shared memory. - if isBonus { - log.Info("skipping atomic tx acceptance on bonus block", "block", b.id) - return vm.db.Commit() - } - - batch, err := vm.db.CommitBatch() + commitBatch, err := b.vm.db.CommitBatch() if err != nil { - return fmt.Errorf("failed to create commit batch due to: %w", err) + return fmt.Errorf("could not create commit batch processing block[%s]: %w", b.ID(), err) } - return vm.ctx.SharedMemory.Apply(batchChainsAndInputs, batch) -} - -// indexAtomics writes given list of atomic transactions and atomic operations to atomic repository -// and atomic trie respectively -func (b *Block) indexAtomics(vm *VM, height uint64, atomicTxs []*Tx, batchChainsAndInputs map[ids.ID]*atomic.Requests, isBonus bool) error { - if isBonus { - // avoid indexing atomic operations of txs on bonus blocks in the trie - // so we do not re-execute them the second time that they appear - return vm.atomicTxRepository.WriteBonus(height, atomicTxs) - } - - if err := vm.atomicTxRepository.Write(height, atomicTxs); err != nil { - return err - } - return b.vm.atomicTrie.Index(height, batchChainsAndInputs) + return atomicState.Accept(commitBatch) } // Reject implements the snowman.Block interface @@ -210,6 +176,14 @@ func (b *Block) Reject() error { log.Debug("Failed to re-issue transaction in rejected block", "txID", tx.ID(), "err", err) } } + atomicState, err := b.vm.atomicBackend.GetVerifiedAtomicState(common.Hash(b.ID())) + if err != nil { + // should never occur since [b] must be verified before calling Reject + return err + } + if err := atomicState.Reject(); err != nil { + return err + } return b.vm.blockChain.Reject(b.ethBlock) } @@ -238,14 +212,14 @@ func (b *Block) Timestamp() time.Time { } // syntacticVerify verifies that a *Block is well-formed. -func (b *Block) syntacticVerify() (params.Rules, error) { +func (b *Block) syntacticVerify() error { if b == nil || b.ethBlock == nil { - return params.Rules{}, errInvalidBlock + return errInvalidBlock } header := b.ethBlock.Header() rules := b.vm.chainConfig.AvalancheRules(header.Number, new(big.Int).SetUint64(header.Time)) - return rules, b.vm.syntacticBlockValidator.SyntacticVerify(b, rules) + return b.vm.syntacticBlockValidator.SyntacticVerify(b, rules) } // Verify implements the snowman.Block interface @@ -254,61 +228,20 @@ func (b *Block) Verify() error { } func (b *Block) verify(writes bool) error { - rules, err := b.syntacticVerify() - if err != nil { + if err := b.syntacticVerify(); err != nil { return fmt.Errorf("syntactic block verification failed: %w", err) } - if err := b.verifyAtomicTxs(rules); err != nil { - return err - } - - return b.vm.blockChain.InsertBlockManual(b.ethBlock, writes) -} - -func (b *Block) verifyAtomicTxs(rules params.Rules) error { - // Ensure that the parent was verified and inserted correctly. - ancestorID := b.Parent() - ancestorHash := common.Hash(ancestorID) - if !b.vm.blockChain.HasBlock(ancestorHash, b.Height()-1) { - return errRejectedParent - } - - // If the ancestor is unknown, then the parent failed verification when - // it was called. - // If the ancestor is rejected, then this block shouldn't be inserted - // into the canonical chain because the parent will be missing. - ancestorInf, err := b.vm.GetBlockInternal(ancestorID) - if err != nil { - return errRejectedParent - } - if blkStatus := ancestorInf.Status(); blkStatus == choices.Unknown || blkStatus == choices.Rejected { - return errRejectedParent - } - ancestor, ok := ancestorInf.(*Block) - if !ok { - return fmt.Errorf("expected %s, parent of %s, to be *Block but is %T", ancestor.ID(), b.ID(), ancestorInf) - } - if bonusBlocks.Contains(b.id) { - log.Info("skipping atomic tx verification on bonus block", "block", b.id) - return nil - } - - // If the tx is an atomic tx, ensure that it doesn't conflict with any of - // its processing ancestry. - inputs := &ids.Set{} - for _, atomicTx := range b.atomicTxs { - utx := atomicTx.UnsignedAtomicTx - if err := utx.SemanticVerify(b.vm, atomicTx, ancestor, b.ethBlock.BaseFee(), rules); err != nil { - return fmt.Errorf("invalid block due to failed semanatic verify: %w at height %d", err, b.Height()) - } - txInputs := utx.InputUTXOs() - if inputs.Overlaps(txInputs) { - return errConflictingAtomicInputs + err := b.vm.blockChain.InsertBlockManual(b.ethBlock, writes) + if err != nil || !writes { + // if an error occurred inserting the block into the chain + // or if we are not pinning to memory, unpin the atomic trie + // changes from memory (if they were pinned). + if atomicState, err := b.vm.atomicBackend.GetVerifiedAtomicState(b.ethBlock.Hash()); err == nil { + _ = atomicState.Reject() // ignore this error so we can return the original error instead. } - inputs.Union(txInputs) } - return nil + return err } // Bytes implements the snowman.Block interface diff --git a/coreth/plugin/evm/block_builder.go b/coreth/plugin/evm/block_builder.go index 7c73f145..333ea88b 100644 --- a/coreth/plugin/evm/block_builder.go +++ b/coreth/plugin/evm/block_builder.go @@ -171,14 +171,15 @@ func (b *blockBuilder) signalTxsReady() { // and notifies the VM when the tx pool has transactions to be // put into a new block. func (b *blockBuilder) awaitSubmittedTxs() { + // txSubmitChan is invoked when new transactions are issued as well as on re-orgs which + // may orphan transactions that were previously in a preferred block. + txSubmitChan := make(chan core.NewTxsEvent) + b.txPool.SubscribeNewTxsEvent(txSubmitChan) + b.shutdownWg.Add(1) go b.ctx.Log.RecoverAndPanic(func() { defer b.shutdownWg.Done() - // txSubmitChan is invoked when new transactions are issued as well as on re-orgs which - // may orphan transactions that were previously in a preferred block. - txSubmitChan := make(chan core.NewTxsEvent) - b.txPool.SubscribeNewTxsEvent(txSubmitChan) for { select { case ethTxsEvent := <-txSubmitChan: diff --git a/coreth/plugin/evm/block_verification.go b/coreth/plugin/evm/block_verification.go index 856b4f82..9202018f 100644 --- a/coreth/plugin/evm/block_verification.go +++ b/coreth/plugin/evm/block_verification.go @@ -261,5 +261,16 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { } } + if rules.IsCortina { + // In Cortina, ExtraStateRoot must not be empty (should contain the root of the atomic trie). + if ethHeader.ExtraStateRoot == (common.Hash{}) { + return fmt.Errorf("%w: ExtraStateRoot must not be empty", errInvalidExtraStateRoot) + } + } else { + // Before Cortina, ExtraStateRoot must be empty. + if ethHeader.ExtraStateRoot != (common.Hash{}) { + return fmt.Errorf("%w: ExtraStateRoot must be empty", errInvalidExtraStateRoot) + } + } return nil } diff --git a/coreth/plugin/evm/export_tx.go b/coreth/plugin/evm/export_tx.go index 7865e867..36941c27 100644 --- a/coreth/plugin/evm/export_tx.go +++ b/coreth/plugin/evm/export_tx.go @@ -4,6 +4,7 @@ package evm import ( + "errors" "fmt" "math/big" @@ -25,8 +26,10 @@ import ( ) var ( - _ UnsignedAtomicTx = &UnsignedExportTx{} - _ secp256k1fx.UnsignedTx = &UnsignedExportTx{} + _ UnsignedAtomicTx = &UnsignedExportTx{} + _ secp256k1fx.UnsignedTx = &UnsignedExportTx{} + errExportNonAVAXInputBanff = errors.New("export input cannot contain non-AVAX in Banff") + errExportNonAVAXOutputBanff = errors.New("export output cannot contain non-AVAX in Banff") ) // UnsignedExportTx is an unsigned ExportTx @@ -75,7 +78,6 @@ func (utx *UnsignedExportTx) Verify( return errWrongBlockchainID } - // SGB-MERGE if rules.IsSongbirdCode && !rules.IsSongbirdTransition { return errExportTxsDisabled } @@ -97,6 +99,9 @@ func (utx *UnsignedExportTx) Verify( if err := in.Verify(); err != nil { return err } + if rules.IsBanff && in.AssetID != ctx.AVAXAssetID { + return errExportNonAVAXInputBanff + } } for _, out := range utx.ExportedOutputs { @@ -107,6 +112,9 @@ func (utx *UnsignedExportTx) Verify( if assetID != ctx.AVAXAssetID && utx.DestinationChain == constants.PlatformChainID { return errWrongChainID } + if rules.IsBanff && assetID != ctx.AVAXAssetID { + return errExportNonAVAXOutputBanff + } } if !avax.IsSortedTransferableOutputs(utx.ExportedOutputs, Codec) { return errOutputsNotSorted @@ -170,7 +178,7 @@ func (utx *UnsignedExportTx) Burned(assetID ids.ID) (uint64, error) { func (utx *UnsignedExportTx) SemanticVerify( vm *VM, stx *Tx, - parent *Block, + _ *Block, baseFee *big.Int, rules params.Rules, ) error { diff --git a/coreth/plugin/evm/export_tx_test.go b/coreth/plugin/evm/export_tx_test.go index d990f9f7..5784472e 100644 --- a/coreth/plugin/evm/export_tx_test.go +++ b/coreth/plugin/evm/export_tx_test.go @@ -1069,13 +1069,6 @@ func TestExportTxAccept(t *testing.T) { } } -func TestExportTxVerifyNil(t *testing.T) { - var exportTx *UnsignedExportTx - if err := exportTx.Verify(NewContext(), apricotRulesPhase0); err == nil { - t.Fatal("Verify should have failed due to nil transaction") - } -} - func TestExportTxVerify(t *testing.T) { var exportAmount uint64 = 10000000 exportTx := &UnsignedExportTx{ @@ -1130,82 +1123,242 @@ func TestExportTxVerify(t *testing.T) { SortEVMInputsAndSigners(exportTx.Ins, emptySigners) ctx := NewContext() - // Test Valid Export Tx - if err := exportTx.Verify(ctx, apricotRulesPhase1); err != nil { - t.Fatalf("Failed to verify valid ExportTx: %s", err) - } - exportTx.NetworkID = testNetworkID + 1 - // Test Incorrect Network ID Errors - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to incorrect network ID") - } - - exportTx.NetworkID = testNetworkID - exportTx.BlockchainID = nonExistentID - // Test Incorrect Blockchain ID Errors - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to incorrect blockchain ID") - } - - exportTx.BlockchainID = testCChainID - exportTx.DestinationChain = nonExistentID - // Test Incorrect Destination Chain ID Errors - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to incorrect destination chain") - } - - exportTx.DestinationChain = testXChainID - exportedOuts := exportTx.ExportedOutputs - exportTx.ExportedOutputs = nil - evmInputs := exportTx.Ins - // Test No Exported Outputs Errors - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to no exported outputs") - } - - exportTx.ExportedOutputs = []*avax.TransferableOutput{exportedOuts[1], exportedOuts[0]} - // Test Unsorted outputs Errors - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to no unsorted exported outputs") - } - exportTx.ExportedOutputs = []*avax.TransferableOutput{exportedOuts[0], nil} - // Test invalid exported output - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to invalid output") - } - - exportTx.ExportedOutputs = []*avax.TransferableOutput{exportedOuts[0], exportedOuts[1]} - exportTx.Ins = []EVMInput{evmInputs[1], evmInputs[0]} - // Test unsorted EVM Inputs passes before AP1 - if err := exportTx.Verify(ctx, apricotRulesPhase0); err != nil { - t.Fatalf("ExportTx should have passed verification before AP1, but failed due to %s", err) - } - // Test unsorted EVM Inputs fails after AP1 - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to unsorted EVM Inputs") - } - exportTx.Ins = []EVMInput{ - { - Address: testEthAddrs[0], - Amount: 0, - AssetID: testAvaxAssetID, - Nonce: 0, + tests := map[string]atomicTxVerifyTest{ + "nil tx": { + generate: func(t *testing.T) UnsignedAtomicTx { + return (*UnsignedExportTx)(nil) + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: errNilTx.Error(), + }, + "valid export tx": { + generate: func(t *testing.T) UnsignedAtomicTx { + return exportTx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: "", + }, + "valid export tx banff": { + generate: func(t *testing.T) UnsignedAtomicTx { + return exportTx + }, + ctx: ctx, + rules: banffRules, + expectedErr: "", + }, + "incorrect networkID": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.NetworkID++ + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: errWrongNetworkID.Error(), + }, + "incorrect blockchainID": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.BlockchainID = nonExistentID + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: errWrongBlockchainID.Error(), + }, + "incorrect destination chain": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.DestinationChain = nonExistentID + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: errWrongChainID.Error(), // TODO make this error more specific to destination not just chainID + }, + "no exported outputs": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.ExportedOutputs = nil + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: errNoExportOutputs.Error(), + }, + "unsorted outputs": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.ExportedOutputs = []*avax.TransferableOutput{ + tx.ExportedOutputs[1], + tx.ExportedOutputs[0], + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: errOutputsNotSorted.Error(), + }, + "invalid exported output": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.ExportedOutputs = []*avax.TransferableOutput{tx.ExportedOutputs[0], nil} + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: "nil transferable output is not valid", + }, + "unsorted EVM inputs before AP1": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.Ins = []EVMInput{ + tx.Ins[1], + tx.Ins[0], + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: "", + }, + "unsorted EVM inputs after AP1": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.Ins = []EVMInput{ + tx.Ins[1], + tx.Ins[0], + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase1, + expectedErr: errInputsNotSortedUnique.Error(), + }, + "EVM input with amount 0": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.Ins = []EVMInput{ + { + Address: testEthAddrs[0], + Amount: 0, + AssetID: testAvaxAssetID, + Nonce: 0, + }, + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: errNoValueInput.Error(), + }, + "non-unique EVM input before AP1": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.Ins = []EVMInput{tx.Ins[0], tx.Ins[0]} + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase0, + expectedErr: "", + }, + "non-unique EVM input after AP1": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.Ins = []EVMInput{tx.Ins[0], tx.Ins[0]} + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase1, + expectedErr: errInputsNotSortedUnique.Error(), + }, + "non-AVAX input Apricot Phase 6": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.Ins = []EVMInput{ + { + Address: testEthAddrs[0], + Amount: 1, + AssetID: nonExistentID, + Nonce: 0, + }, + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase6, + expectedErr: "", + }, + "non-AVAX output Apricot Phase 6": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.ExportedOutputs = []*avax.TransferableOutput{ + { + Asset: avax.Asset{ID: nonExistentID}, + Out: &secp256k1fx.TransferOutput{ + Amt: exportAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{testShortIDAddrs[0]}, + }, + }, + }, + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase6, + expectedErr: "", + }, + "non-AVAX input Banff": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.Ins = []EVMInput{ + { + Address: testEthAddrs[0], + Amount: 1, + AssetID: nonExistentID, + Nonce: 0, + }, + } + return &tx + }, + ctx: ctx, + rules: banffRules, + expectedErr: errExportNonAVAXInputBanff.Error(), + }, + "non-AVAX output Banff": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *exportTx + tx.ExportedOutputs = []*avax.TransferableOutput{ + { + Asset: avax.Asset{ID: nonExistentID}, + Out: &secp256k1fx.TransferOutput{ + Amt: exportAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{testShortIDAddrs[0]}, + }, + }, + }, + } + return &tx + }, + ctx: ctx, + rules: banffRules, + expectedErr: errExportNonAVAXOutputBanff.Error(), }, } - // Test ExportTx with invalid EVM Input amount 0 fails verification - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to 0 value amount") - } - exportTx.Ins = []EVMInput{evmInputs[0], evmInputs[0]} - // Test non-unique EVM Inputs passes verification before AP1 - if err := exportTx.Verify(ctx, apricotRulesPhase0); err != nil { - t.Fatalf("ExportTx with non-unique EVM Inputs should have passed verification prior to AP1, but failed due to %s", err) - } - exportTx.Ins = []EVMInput{evmInputs[0], evmInputs[0]} - // Test non-unique EVM Inputs fails verification after AP1 - if err := exportTx.Verify(ctx, apricotRulesPhase1); err == nil { - t.Fatal("ExportTx should have failed verification due to non-unique inputs") + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + executeTxVerifyTest(t, test) + }) } } diff --git a/coreth/plugin/evm/import_tx.go b/coreth/plugin/evm/import_tx.go index e9c0803e..b641ec52 100644 --- a/coreth/plugin/evm/import_tx.go +++ b/coreth/plugin/evm/import_tx.go @@ -4,6 +4,7 @@ package evm import ( + "errors" "fmt" "math/big" @@ -23,8 +24,10 @@ import ( ) var ( - _ UnsignedAtomicTx = &UnsignedImportTx{} - _ secp256k1fx.UnsignedTx = &UnsignedImportTx{} + _ UnsignedAtomicTx = &UnsignedImportTx{} + _ secp256k1fx.UnsignedTx = &UnsignedImportTx{} + errImportNonAVAXInputBanff = errors.New("import input cannot contain non-AVAX in Banff") + errImportNonAVAXOutputBanff = errors.New("import output cannot contain non-AVAX in Banff") ) // UnsignedImportTx is an unsigned ImportTx @@ -69,7 +72,6 @@ func (utx *UnsignedImportTx) Verify( return errNoEVMOutputs } - // SGB-MERGE if rules.IsSongbirdCode && !rules.IsSongbirdTransition { return errImportTxsDisabled } @@ -91,12 +93,18 @@ func (utx *UnsignedImportTx) Verify( if err := out.Verify(); err != nil { return fmt.Errorf("EVM Output failed verification: %w", err) } + if rules.IsBanff && out.AssetID != ctx.AVAXAssetID { + return errImportNonAVAXOutputBanff + } } for _, in := range utx.ImportedInputs { if err := in.Verify(); err != nil { return fmt.Errorf("atomic input failed verification: %w", err) } + if rules.IsBanff && in.AssetID() != ctx.AVAXAssetID { + return errImportNonAVAXInputBanff + } } if !avax.IsSortedAndUniqueTransferableInputs(utx.ImportedInputs) { return errInputsNotSortedUnique diff --git a/coreth/plugin/evm/import_tx_test.go b/coreth/plugin/evm/import_tx_test.go index 969a0919..861636de 100644 --- a/coreth/plugin/evm/import_tx_test.go +++ b/coreth/plugin/evm/import_tx_test.go @@ -132,7 +132,15 @@ func TestImportTxVerify(t *testing.T) { }, ctx: ctx, rules: apricotRulesPhase0, - expectedErr: "", // Expect this transaction to be valid + expectedErr: "", // Expect this transaction to be valid in Apricot Phase 0 + }, + "valid import tx banff": { + generate: func(t *testing.T) UnsignedAtomicTx { + return importTx + }, + ctx: ctx, + rules: banffRules, + expectedErr: "", // Expect this transaction to be valid in Banff }, "invalid network ID": { generate: func(t *testing.T) UnsignedAtomicTx { @@ -323,6 +331,86 @@ func TestImportTxVerify(t *testing.T) { rules: apricotRulesPhase3, expectedErr: errNoEVMOutputs.Error(), }, + "non-AVAX input Apricot Phase 6": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *importTx + tx.ImportedInputs = []*avax.TransferableInput{ + { + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: uint32(0), + }, + Asset: avax.Asset{ID: ids.GenerateTestID()}, + In: &secp256k1fx.TransferInput{ + Amt: importAmount, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase6, + expectedErr: "", + }, + "non-AVAX output Apricot Phase 6": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *importTx + tx.Outs = []EVMOutput{ + { + Address: importTx.Outs[0].Address, + Amount: importTx.Outs[0].Amount, + AssetID: ids.GenerateTestID(), + }, + } + return &tx + }, + ctx: ctx, + rules: apricotRulesPhase6, + expectedErr: "", + }, + "non-AVAX input Banff": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *importTx + tx.ImportedInputs = []*avax.TransferableInput{ + { + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: uint32(0), + }, + Asset: avax.Asset{ID: ids.GenerateTestID()}, + In: &secp256k1fx.TransferInput{ + Amt: importAmount, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + } + return &tx + }, + ctx: ctx, + rules: banffRules, + expectedErr: errImportNonAVAXInputBanff.Error(), + }, + "non-AVAX output Banff": { + generate: func(t *testing.T) UnsignedAtomicTx { + tx := *importTx + tx.Outs = []EVMOutput{ + { + Address: importTx.Outs[0].Address, + Amount: importTx.Outs[0].Amount, + AssetID: ids.GenerateTestID(), + }, + } + return &tx + }, + ctx: ctx, + rules: banffRules, + expectedErr: errImportNonAVAXOutputBanff.Error(), + }, } for name, test := range tests { t.Run(name, func(t *testing.T) { diff --git a/coreth/plugin/evm/log.go b/coreth/plugin/evm/log.go index abcf66df..cd1b796a 100644 --- a/coreth/plugin/evm/log.go +++ b/coreth/plugin/evm/log.go @@ -75,6 +75,8 @@ func CorethJSONFormat(alias string) log.Format { if !ok { props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) } else { + // The number of arguments is normalized from the geth logger + // to ensure that this will not cause an index out of bounds error props[k] = formatJSONValue(r.Ctx[i+1]) } } diff --git a/coreth/plugin/evm/message/handler.go b/coreth/plugin/evm/message/handler.go index 15886725..cfa97e7e 100644 --- a/coreth/plugin/evm/message/handler.go +++ b/coreth/plugin/evm/message/handler.go @@ -52,7 +52,6 @@ type ResponseHandler interface { // OnResponse is invoked when the peer responded to a request OnResponse(nodeID ids.NodeID, requestID uint32, response []byte) error // OnFailure is invoked when there was a failure in processing a request - // The FailureReason outlines the underlying cause. OnFailure(nodeID ids.NodeID, requestID uint32) error } diff --git a/coreth/plugin/evm/syncervm_client.go b/coreth/plugin/evm/syncervm_client.go index 97321fd9..0cc36591 100644 --- a/coreth/plugin/evm/syncervm_client.go +++ b/coreth/plugin/evm/syncervm_client.go @@ -51,7 +51,7 @@ type stateSyncClientConfig struct { metadataDB database.Database acceptedBlockDB database.Database db *versiondb.Database - atomicTrie AtomicTrie + atomicBackend AtomicBackend client syncclient.Client @@ -274,11 +274,14 @@ func (client *stateSyncerClient) syncBlocks(ctx context.Context, fromHash common func (client *stateSyncerClient) syncAtomicTrie(ctx context.Context) error { log.Info("atomic tx: sync starting", "root", client.syncSummary.AtomicRoot) - atomicSyncer := client.atomicTrie.Syncer(client.client, client.syncSummary.AtomicRoot, client.syncSummary.BlockNumber) + atomicSyncer, err := client.atomicBackend.Syncer(client.client, client.syncSummary.AtomicRoot, client.syncSummary.BlockNumber) + if err != nil { + return err + } if err := atomicSyncer.Start(ctx); err != nil { return err } - err := <-atomicSyncer.Done() + err = <-atomicSyncer.Done() log.Info("atomic tx: sync finished", "root", client.syncSummary.AtomicRoot, "err", err) return err } @@ -368,7 +371,7 @@ func (client *stateSyncerClient) finishSync() error { // ApplyToSharedMemory does this, and even if the VM is stopped // (gracefully or ungracefully), since MarkApplyToSharedMemoryCursor // is called, VM will resume ApplyToSharedMemory on Initialize. - return client.atomicTrie.ApplyToSharedMemory(block.NumberU64()) + return client.atomicBackend.ApplyToSharedMemory(block.NumberU64()) } // updateVMMarkers updates the following markers in the VM's database @@ -381,9 +384,10 @@ func (client *stateSyncerClient) updateVMMarkers() error { // Mark the previously last accepted block for the shared memory cursor, so that we will execute shared // memory operations from the previously last accepted block to [vm.syncSummary] when ApplyToSharedMemory // is called. - if err := client.atomicTrie.MarkApplyToSharedMemoryCursor(client.lastAcceptedHeight); err != nil { + if err := client.atomicBackend.MarkApplyToSharedMemoryCursor(client.lastAcceptedHeight); err != nil { return err } + client.atomicBackend.SetLastAccepted(client.syncSummary.BlockHash) if err := client.acceptedBlockDB.Put(lastAcceptedKey, client.syncSummary.BlockHash[:]); err != nil { return err } diff --git a/coreth/plugin/evm/syncervm_test.go b/coreth/plugin/evm/syncervm_test.go index 8eab466b..4ca4f5a0 100644 --- a/coreth/plugin/evm/syncervm_test.go +++ b/coreth/plugin/evm/syncervm_test.go @@ -303,11 +303,12 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { } }) - // override atomicTrie's commitHeightInterval so the call to [atomicTrie.Index] + // override serverAtomicTrie's commitInterval so the call to [serverAtomicTrie.Index] // creates a commit at the height [syncableInterval]. This is necessary to support // fetching a state summary. - serverVM.atomicTrie.(*atomicTrie).commitHeightInterval = test.syncableInterval - assert.NoError(t, serverVM.atomicTrie.Index(test.syncableInterval, nil)) + serverAtomicTrie := serverVM.atomicTrie.(*atomicTrie) + serverAtomicTrie.commitInterval = test.syncableInterval + assert.NoError(t, serverAtomicTrie.commit(test.syncableInterval, serverAtomicTrie.LastAcceptedRoot())) assert.NoError(t, serverVM.db.Commit()) serverSharedMemories := newSharedMemories(serverAtomicMemory, serverVM.ctx.ChainID, serverVM.ctx.XChainID) @@ -356,6 +357,9 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { assert.NoError(t, err) assert.True(t, enabled) + // override [syncerVM]'s commit interval so the atomic trie works correctly. + syncerVM.atomicTrie.(*atomicTrie).commitInterval = test.syncableInterval + // override [serverVM]'s SendAppResponse function to trigger AppResponse on [syncerVM] serverAppSender.SendAppResponseF = func(nodeID ids.NodeID, requestID uint32, response []byte) error { if test.responseIntercept == nil { diff --git a/coreth/plugin/evm/tx_test.go b/coreth/plugin/evm/tx_test.go index 79a36311..cb59f20a 100644 --- a/coreth/plugin/evm/tx_test.go +++ b/coreth/plugin/evm/tx_test.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/coreth/params" + "github.com/stretchr/testify/require" ) func TestCalculateDynamicFee(t *testing.T) { @@ -59,19 +60,13 @@ type atomicTxVerifyTest struct { // executeTxVerifyTest tests func executeTxVerifyTest(t *testing.T, test atomicTxVerifyTest) { + require := require.New(t) atomicTx := test.generate(t) err := atomicTx.Verify(test.ctx, test.rules) if len(test.expectedErr) == 0 { - if err != nil { - t.Fatalf("Atomic tx failed unexpectedly due to: %s", err) - } + require.NoError(err) } else { - if err == nil { - t.Fatalf("Expected atomic tx test to fail due to: %s, but passed verification", test.expectedErr) - } - if !strings.Contains(err.Error(), test.expectedErr) { - t.Fatalf("Expected Verify to fail due to %s, but failed with: %s", test.expectedErr, err) - } + require.ErrorContains(err, test.expectedErr, "expected tx verify to fail with specified error") } } diff --git a/coreth/plugin/evm/version.go b/coreth/plugin/evm/version.go index b1d6e52a..427e6a10 100644 --- a/coreth/plugin/evm/version.go +++ b/coreth/plugin/evm/version.go @@ -11,7 +11,7 @@ var ( // GitCommit is set by the build script GitCommit string // Version is the version of Coreth - Version string = "v0.8.15" + Version string = "v0.11.0" ) func init() { diff --git a/coreth/plugin/evm/vm.go b/coreth/plugin/evm/vm.go index 887d7a42..2f355aaa 100644 --- a/coreth/plugin/evm/vm.go +++ b/coreth/plugin/evm/vm.go @@ -13,7 +13,6 @@ import ( "math/big" "os" "path/filepath" - "sort" "strings" "sync" "time" @@ -175,6 +174,7 @@ var ( errConflictingAtomicTx = errors.New("conflicting atomic tx present") errTooManyAtomicTx = errors.New("too many atomic tx") errMissingAtomicTxs = errors.New("cannot build a block with non-empty extra data and zero atomic transactions") + errInvalidExtraStateRoot = errors.New("invalid ExtraStateRoot") errImportTxsDisabled = errors.New("import transactions are disabled") errExportTxsDisabled = errors.New("export transactions are disabled") ) @@ -251,6 +251,8 @@ type VM struct { atomicTxRepository AtomicTxRepository // [atomicTrie] maintains a merkle forest of [height]=>[atomic txs]. atomicTrie AtomicTrie + // [atomicBackend] abstracts verification and processing of atomic transactions + atomicBackend AtomicBackend builder *blockBuilder @@ -378,17 +380,23 @@ func (vm *VM) Initialize( case g.Config.ChainID.Cmp(params.AvalancheMainnetChainID) == 0: g.Config = params.AvalancheMainnetChainConfig extDataHashes = mainnetExtDataHashes + case g.Config.ChainID.Cmp(params.FlareChainID) == 0: + g.Config = params.FlareChainConfig + case g.Config.ChainID.Cmp(params.SongbirdChainID) == 0: + g.Config = params.SongbirdChainConfig + case g.Config.ChainID.Cmp(params.CostwoChainID) == 0: + g.Config = params.CostwoChainConfig + case g.Config.ChainID.Cmp(params.CostonChainID) == 0: + g.Config = params.CostonChainConfig + case g.Config.ChainID.Cmp(params.LocalFlareChainID) == 0: + g.Config = params.LocalFlareChainConfig + case g.Config.ChainID.Cmp(params.LocalChainID) == 0: + g.Config = params.LocalChainConfig case g.Config.ChainID.Cmp(params.AvalancheFujiChainID) == 0: g.Config = params.AvalancheFujiChainConfig extDataHashes = fujiExtDataHashes case g.Config.ChainID.Cmp(params.AvalancheLocalChainID) == 0: g.Config = params.AvalancheLocalChainConfig - case g.Config.ChainID.Cmp(params.CostonChainID) == 0: - g.Config = params.CostonChainConfig - case g.Config.ChainID.Cmp(params.SongbirdChainID) == 0: - g.Config = params.SongbirdChainConfig - case g.Config.ChainID.Cmp(params.LocalChainID) == 0: - g.Config = params.LocalChainConfig } vm.syntacticBlockValidator = NewBlockValidator(extDataHashes) @@ -476,28 +484,32 @@ func (vm *VM) Initialize( if err := vm.initializeChain(lastAcceptedHash); err != nil { return err } - - // initialize atomic repository - vm.atomicTxRepository, err = NewAtomicTxRepository(vm.db, vm.codec, lastAcceptedHeight) - if err != nil { - return fmt.Errorf("failed to create atomic repository: %w", err) - } - bonusBlockHeights := make(map[uint64]ids.ID) + // initialize bonus blocks on mainnet + var ( + bonusBlockHeights map[uint64]ids.ID + canonicalBlockHeights []uint64 + ) if vm.chainID.Cmp(params.AvalancheMainnetChainID) == 0 { bonusBlockHeights = bonusBlockMainnetHeights + canonicalBlockHeights = canonicalBlockMainnetHeights } - if err := repairAtomicRepositoryForBonusBlockTxs( - vm.atomicTxRepository, - vm.db, - getAtomicRepositoryRepairHeights(vm.chainID), + + // initialize atomic repository + vm.atomicTxRepository, err = NewAtomicTxRepository( + vm.db, vm.codec, lastAcceptedHeight, + bonusBlockHeights, canonicalBlockHeights, vm.getAtomicTxFromPreApricot5BlockByHeight, - ); err != nil { - return fmt.Errorf("failed to repair atomic repository: %w", err) + ) + if err != nil { + return fmt.Errorf("failed to create atomic repository: %w", err) } - vm.atomicTrie, err = NewAtomicTrie(vm.db, vm.ctx.SharedMemory, bonusBlockHeights, vm.atomicTxRepository, vm.codec, lastAcceptedHeight, vm.config.CommitInterval) + vm.atomicBackend, err = NewAtomicBackend( + vm.db, vm.ctx.SharedMemory, bonusBlockHeights, vm.atomicTxRepository, lastAcceptedHeight, lastAcceptedHash, vm.config.CommitInterval, + ) if err != nil { - return fmt.Errorf("failed to create atomic trie: %w", err) + return fmt.Errorf("failed to create atomic backend: %w", err) } + vm.atomicTrie = vm.atomicBackend.AtomicTrie() go vm.ctx.Log.RecoverAndPanic(vm.startContinuousProfiler) @@ -604,7 +616,7 @@ func (vm *VM) initializeStateSyncClient(lastAcceptedHeight uint64) error { metadataDB: vm.metadataDB, acceptedBlockDB: vm.acceptedBlockDB, db: vm.db, - atomicTrie: vm.atomicTrie, + atomicBackend: vm.atomicBackend, toEngine: vm.toEngine, }) @@ -781,6 +793,19 @@ func (vm *VM) postBatchOnFinalizeAndAssemble(header *types.Header, state *state. size += txSize } + // In Cortina the block header must include the atomic trie root. + if rules.IsCortina { + // Pass common.Hash{} as the current block's hash to the atomic backend, this avoids + // pinning changes to the atomic trie in memory, as we are still computing the header + // for this block and don't have its hash yet. Here we calculate the root of the atomic + // trie to store in the block header. + atomicTrieRoot, err := vm.atomicBackend.InsertTxs(common.Hash{}, header.Number.Uint64(), header.ParentHash, batchAtomicTxs) + if err != nil { + return nil, nil, nil, err + } + header.ExtraStateRoot = atomicTrieRoot + } + // If there is a non-zero number of transactions, marshal them and return the byte slice // for the block's extra data along with the contribution and gas used. if len(batchAtomicTxs) > 0 { @@ -817,17 +842,41 @@ func (vm *VM) onExtraStateChange(block *types.Block, state *state.StateDB) (*big var ( batchContribution *big.Int = big.NewInt(0) batchGasUsed *big.Int = big.NewInt(0) - timestamp = new(big.Int).SetUint64(block.Time()) - isApricotPhase4 = vm.chainConfig.IsApricotPhase4(timestamp) - isApricotPhase5 = vm.chainConfig.IsApricotPhase5(timestamp) + header = block.Header() + rules = vm.chainConfig.AvalancheRules(header.Number, new(big.Int).SetUint64(header.Time)) ) - txs, err := ExtractAtomicTxs(block.ExtData(), isApricotPhase5, vm.codec) + txs, err := ExtractAtomicTxs(block.ExtData(), rules.IsApricotPhase5, vm.codec) if err != nil { return nil, nil, err } - // If there are no transactions, we can return early + // If [atomicBackend] is nil, the VM is still initializing and is reprocessing accepted blocks. + if vm.atomicBackend != nil { + if vm.atomicBackend.IsBonus(block.NumberU64(), block.Hash()) { + log.Info("skipping atomic tx verification on bonus block", "block", block.Hash()) + } else { + // Verify [txs] do not conflict with themselves or ancestor blocks. + if err := vm.verifyTxs(txs, block.ParentHash(), block.BaseFee(), block.NumberU64(), rules); err != nil { + return nil, nil, err + } + } + // Update the atomic backend with [txs] from this block. + atomicRoot, err := vm.atomicBackend.InsertTxs(block.Hash(), block.NumberU64(), block.ParentHash(), txs) + if err != nil { + return nil, nil, err + } + if rules.IsCortina { + // In Cortina, the atomic trie root should be in ExtraStateRoot. + if header.ExtraStateRoot != atomicRoot { + return nil, nil, fmt.Errorf( + "%w: (expected %s) (got %s)", errInvalidExtraStateRoot, header.ExtraStateRoot, atomicRoot, + ) + } + } + } + + // If there are no transactions, we can return early. if len(txs) == 0 { return nil, nil, nil } @@ -837,8 +886,8 @@ func (vm *VM) onExtraStateChange(block *types.Block, state *state.StateDB) (*big return nil, nil, err } // If ApricotPhase4 is enabled, calculate the block fee contribution - if isApricotPhase4 { - contribution, gasUsed, err := tx.BlockFeeContribution(isApricotPhase5, vm.ctx.AVAXAssetID, block.BaseFee()) + if rules.IsApricotPhase4 { + contribution, gasUsed, err := tx.BlockFeeContribution(rules.IsApricotPhase5, vm.ctx.AVAXAssetID, block.BaseFee()) if err != nil { return nil, nil, err } @@ -849,7 +898,7 @@ func (vm *VM) onExtraStateChange(block *types.Block, state *state.StateDB) (*big // If ApricotPhase5 is enabled, enforce that the atomic gas used does not exceed the // atomic gas limit. - if vm.chainConfig.IsApricotPhase5(timestamp) { + if rules.IsApricotPhase5 { // Ensure that [tx] does not push [block] above the atomic gas limit. if batchGasUsed.Cmp(params.AtomicGasLimit) == 1 { return nil, nil, fmt.Errorf("atomic gas used (%d) by block (%s), exceeds atomic gas limit (%d)", batchGasUsed, block.Hash().Hex(), params.AtomicGasLimit) @@ -1008,7 +1057,7 @@ func (vm *VM) parseBlock(b []byte) (snowman.Block, error) { } // Performing syntactic verification in ParseBlock allows for // short-circuiting bad blocks before they are processed by the VM. - if _, err := block.syntacticVerify(); err != nil { + if err := block.syntacticVerify(); err != nil { return nil, fmt.Errorf("syntactic block verification failed: %w", err) } return block, nil @@ -1055,13 +1104,18 @@ func (vm *VM) VerifyHeightIndex() error { return nil } -// GetBlockIDAtHeight retrieves the blkID of the canonical block at [blkHeight] -// if [blkHeight] is less than the height of the last accepted block, this will return -// a canonical block. Otherwise, it may return a blkID that has not yet been accepted. +// GetBlockAtHeight implements the HeightIndexedChainVM interface and returns the +// canonical block at [blkHeight]. +// If [blkHeight] is less than the height of the last accepted block, this will return +// the block accepted at that height. Otherwise, it may return a blkID that has not yet +// been accepted. +// Note: the engine assumes that if a block is not found at [blkHeight], then +// [database.ErrNotFound] will be returned. This indicates that the VM has state synced +// and does not have all historical blocks available. func (vm *VM) GetBlockIDAtHeight(blkHeight uint64) (ids.ID, error) { ethBlock := vm.blockChain.GetBlockByNumber(blkHeight) if ethBlock == nil { - return ids.ID{}, fmt.Errorf("could not find block at height: %d", blkHeight) + return ids.ID{}, database.ErrNotFound } return ids.ID(ethBlock.Hash()), nil @@ -1332,6 +1386,48 @@ func (vm *VM) verifyTx(tx *Tx, parentHash common.Hash, baseFee *big.Int, state * return tx.UnsignedAtomicTx.EVMStateTransfer(vm.ctx, state) } +// verifyTxs verifies that [txs] are valid to be issued into a block with parent block [parentHash] +// using [rules] as the current rule set. +func (vm *VM) verifyTxs(txs []*Tx, parentHash common.Hash, baseFee *big.Int, height uint64, rules params.Rules) error { + // Ensure that the parent was verified and inserted correctly. + if !vm.blockChain.HasBlock(parentHash, height-1) { + return errRejectedParent + } + + ancestorID := ids.ID(parentHash) + // If the ancestor is unknown, then the parent failed verification when + // it was called. + // If the ancestor is rejected, then this block shouldn't be inserted + // into the canonical chain because the parent will be missing. + ancestorInf, err := vm.GetBlockInternal(ancestorID) + if err != nil { + return errRejectedParent + } + if blkStatus := ancestorInf.Status(); blkStatus == choices.Unknown || blkStatus == choices.Rejected { + return errRejectedParent + } + ancestor, ok := ancestorInf.(*Block) + if !ok { + return fmt.Errorf("expected parent block %s, to be *Block but is %T", ancestor.ID(), ancestorInf) + } + + // Ensure each tx in [txs] doesn't conflict with any other atomic tx in + // a processing ancestor block. + inputs := &ids.Set{} + for _, atomicTx := range txs { + utx := atomicTx.UnsignedAtomicTx + if err := utx.SemanticVerify(vm, atomicTx, ancestor, baseFee, rules); err != nil { + return fmt.Errorf("invalid block due to failed semanatic verify: %w at height %d", err, height) + } + txInputs := utx.InputUTXOs() + if inputs.Overlaps(txInputs) { + return errConflictingAtomicInputs + } + inputs.Union(txInputs) + } + return nil +} + // GetAtomicUTXOs returns the utxos that at least one of the provided addresses is // referenced in. func (vm *VM) GetAtomicUTXOs( @@ -1600,23 +1696,6 @@ func (vm *VM) estimateBaseFee(ctx context.Context) (*big.Int, error) { return baseFee, nil } -func getAtomicRepositoryRepairHeights(chainID *big.Int) []uint64 { - if chainID.Cmp(params.AvalancheMainnetChainID) != 0 { - return nil - } - repairHeights := make([]uint64, 0, len(bonusBlockMainnetHeights)+len(canonicalBonusBlocks)) - for height := range bonusBlockMainnetHeights { - repairHeights = append(repairHeights, height) - } - for _, height := range canonicalBonusBlocks { - if _, exists := bonusBlockMainnetHeights[height]; !exists { - repairHeights = append(repairHeights, height) - } - } - sort.Slice(repairHeights, func(i, j int) bool { return repairHeights[i] < repairHeights[j] }) - return repairHeights -} - func (vm *VM) getAtomicTxFromPreApricot5BlockByHeight(height uint64) (*Tx, error) { blk := vm.blockChain.GetBlockByNumber(height) if blk == nil { @@ -1625,63 +1704,6 @@ func (vm *VM) getAtomicTxFromPreApricot5BlockByHeight(height uint64) (*Tx, error return ExtractAtomicTx(blk.ExtData(), vm.codec) } -// repairAtomicRepositoryForBonusBlockTxs ensures that atomic txs that were processed -// on more than one block (canonical block + a number of bonus blocks) are indexed to -// the first height they were processed on (canonical block). -// [sortedHeights] should include all canonical block + bonus block heights in ascending -// order, and will only be passed as non-empty on mainnet. -func repairAtomicRepositoryForBonusBlockTxs( - atomicTxRepository AtomicTxRepository, db *versiondb.Database, - sortedHeights []uint64, getAtomicTxFromBlockByHeight func(height uint64) (*Tx, error), -) error { - done, err := atomicTxRepository.IsBonusBlocksRepaired() - if err != nil { - return err - } - if done { - return nil - } - repairedEntries := uint64(0) - seenTxs := make(map[ids.ID][]uint64) - for _, height := range sortedHeights { - // get atomic tx from block - tx, err := getAtomicTxFromBlockByHeight(height) - if err != nil { - return err - } - if tx == nil { - continue - } - - // get the tx by txID and update it, the first time we encounter - // a given [txID], overwrite the previous [txID] => [height] - // mapping. This provides a canonical mapping across nodes. - heights, seen := seenTxs[tx.ID()] - _, foundHeight, err := atomicTxRepository.GetByTxID(tx.ID()) - if err != nil && !errors.Is(err, database.ErrNotFound) { - return err - } - if !seen { - if err := atomicTxRepository.Write(height, []*Tx{tx}); err != nil { - return err - } - } else { - if err := atomicTxRepository.WriteBonus(height, []*Tx{tx}); err != nil { - return err - } - } - if foundHeight != height && !seen { - repairedEntries++ - } - seenTxs[tx.ID()] = append(heights, height) - } - if err := atomicTxRepository.MarkBonusBlocksRepaired(repairedEntries); err != nil { - return err - } - log.Info("repairAtomicRepositoryForBonusBlockTxs complete", "repairedEntries", repairedEntries) - return db.Commit() -} - // readLastAccepted reads the last accepted hash from [acceptedBlockDB] and returns the // last accepted block hash and height by reading directly from [vm.chaindb] instead of relying // on [chain]. diff --git a/coreth/plugin/evm/vm_extra_state_root_test.go b/coreth/plugin/evm/vm_extra_state_root_test.go new file mode 100644 index 00000000..2bbefe97 --- /dev/null +++ b/coreth/plugin/evm/vm_extra_state_root_test.go @@ -0,0 +1,267 @@ +// (c) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "encoding/json" + "math/big" + "testing" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/utils/crypto" + "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/trie" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +var ( + // testCortinaTime is an arbitrary time used to test the VM's behavior when + // Cortina activates. + testCortinaTime = time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) + // testCortinaJSON is a modified genesisJSONCortina to include the Cortina + // upgrade at testCortinaTime. + testCortinaJSON string +) + +func init() { + var genesis core.Genesis + if err := json.Unmarshal([]byte(genesisJSONCortina), &genesis); err != nil { + panic(err) + } + genesis.Config.CortinaBlockTimestamp = big.NewInt(testCortinaTime.Unix()) + json, err := json.Marshal(genesis) + if err != nil { + panic(err) + } + testCortinaJSON = string(json) +} + +type verifyExtraStateRootConfig struct { + genesis string + blockTime1 time.Time + blockTime2 time.Time + expectedExtraStateRoot func(atomicRoot1, atomicRoot2 common.Hash) (common.Hash, common.Hash) +} + +// testVerifyExtraState root builds 2 blocks using a vm with [test.genesis]. +// First block is built at [blockTime1] and includes an import tx. +// Second block is build at [blockTime2] and includes an export tx. +// After blocks build, [test.expectedExtraStateRoot] is called with the roots +// of the atomic trie at block1 and block2 and the ExtraStateRoot field of +// the blocks are checked against the return value of that function. +func testVerifyExtraStateRoot(t *testing.T, test verifyExtraStateRootConfig) { + importAmount := uint64(50000000) + issuer, vm, _, _, _ := GenesisVMWithUTXOs(t, true, test.genesis, "", "", map[ids.ShortID]uint64{ + testShortIDAddrs[0]: importAmount, + }) + defer func() { + if err := vm.Shutdown(); err != nil { + t.Fatal(err) + } + }() + + // issue tx for block1 + vm.clock.Set(test.blockTime1) + importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) + if err != nil { + t.Fatal(err) + } + if err := vm.issueTx(importTx, true /*=local*/); err != nil { + t.Fatal(err) + } + + // build block1 + <-issuer + blk, err := vm.BuildBlock() + if err != nil { + t.Fatal(err) + } + if err := blk.Verify(); err != nil { + t.Fatal(err) + } + if status := blk.Status(); status != choices.Processing { + t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) + } + if err := vm.SetPreference(blk.ID()); err != nil { + t.Fatal(err) + } + if err := blk.Accept(); err != nil { + t.Fatal(err) + } + if status := blk.Status(); status != choices.Accepted { + t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) + } + if lastAcceptedID, err := vm.LastAccepted(); err != nil { + t.Fatal(err) + } else if lastAcceptedID != blk.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) + } + + // issue tx for block2 + vm.clock.Set(test.blockTime2) + exportAmount := importAmount / 2 + exportTx, err := vm.newExportTx(vm.ctx.AVAXAssetID, exportAmount, vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) + if err != nil { + t.Fatal(err) + } + if err := vm.issueTx(exportTx, true /*=local*/); err != nil { + t.Fatal(err) + } + + // build block2 + <-issuer + blk2, err := vm.BuildBlock() + if err != nil { + t.Fatal(err) + } + if err := blk2.Verify(); err != nil { + t.Fatal(err) + } + if status := blk2.Status(); status != choices.Processing { + t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) + } + if err := blk2.Accept(); err != nil { + t.Fatal(err) + } + if status := blk2.Status(); status != choices.Accepted { + t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) + } + if lastAcceptedID, err := vm.LastAccepted(); err != nil { + t.Fatal(err) + } else if lastAcceptedID != blk2.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk2.ID(), lastAcceptedID) + } + + // Check that both atomic transactions were indexed as expected. + indexedImportTx, status, height, err := vm.getAtomicTx(importTx.ID()) + assert.NoError(t, err) + assert.Equal(t, Accepted, status) + assert.Equal(t, uint64(1), height, "expected height of indexed import tx to be 1") + assert.Equal(t, indexedImportTx.ID(), importTx.ID(), "expected ID of indexed import tx to match original txID") + + indexedExportTx, status, height, err := vm.getAtomicTx(exportTx.ID()) + assert.NoError(t, err) + assert.Equal(t, Accepted, status) + assert.Equal(t, uint64(2), height, "expected height of indexed export tx to be 2") + assert.Equal(t, indexedExportTx.ID(), exportTx.ID(), "expected ID of indexed import tx to match original txID") + + // Open an empty trie to re-create the expected atomic trie roots + trie, err := vm.atomicTrie.OpenTrie(common.Hash{}) + if err != nil { + t.Fatal(err) + } + assert.NoError(t, vm.atomicTrie.UpdateTrie(trie, blk.Height(), importTx.mustAtomicOps())) + atomicRootBlock1 := trie.Hash() + assert.NoError(t, vm.atomicTrie.UpdateTrie(trie, blk2.Height(), exportTx.mustAtomicOps())) + atomicRootBlock2 := trie.Hash() + assert.NotZero(t, atomicRootBlock1) + assert.NotZero(t, atomicRootBlock2) + assert.NotEqual(t, atomicRootBlock1, atomicRootBlock2) + + // verify atomic trie roots included in block header. + extraStateRoot := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock.Header().ExtraStateRoot + extraStateRoot2 := blk2.(*chain.BlockWrapper).Block.(*Block).ethBlock.Header().ExtraStateRoot + expectedRoot1, expectedRoot2 := test.expectedExtraStateRoot(atomicRootBlock1, atomicRootBlock2) + assert.Equal(t, expectedRoot1, extraStateRoot) + assert.Equal(t, expectedRoot2, extraStateRoot2) +} + +// Verifies the root of the atomic trie is inclued in Cortina blocks. +func TestIssueAtomicTxsCortina(t *testing.T) { + testVerifyExtraStateRoot(t, verifyExtraStateRootConfig{ + genesis: genesisJSONCortina, + blockTime1: time.Unix(0, 0), // genesis + blockTime2: time.Unix(2, 0), // a bit after, for fee purposes. + expectedExtraStateRoot: func(atomicRoot1, atomicRoot2 common.Hash) (common.Hash, common.Hash) { + return atomicRoot1, atomicRoot2 // we expect both blocks to contain the atomic trie roots respectively. + }, + }) +} + +// Verifies the root of the atomic trie is inclued in the first Cortina block. +func TestIssueAtomicTxsCortinaTransition(t *testing.T) { + testVerifyExtraStateRoot(t, verifyExtraStateRootConfig{ + genesis: testCortinaJSON, + blockTime1: testCortinaTime.Add(-2 * time.Second), // a little before Cortina, so we can test next block at the upgrade timestamp + blockTime2: testCortinaTime, // at the upgrade timestamp + expectedExtraStateRoot: func(atomicRoot1, atomicRoot2 common.Hash) (common.Hash, common.Hash) { + return common.Hash{}, atomicRoot2 // we only expect the Cortina block to include the atomic trie root. + }, + }) +} + +// Calling Verify should not succeed if the proper ExtraStateRoot is not included in a Cortina block. +// Calling Verify should not succeed if ExtraStateRoot is not empty pre-Cortina +func TestCortinaInvalidExtraStateRootWillNotVerify(t *testing.T) { + importAmount := uint64(50000000) + issuer, vm, _, _, _ := GenesisVMWithUTXOs(t, true, testCortinaJSON, "", "", map[ids.ShortID]uint64{ + testShortIDAddrs[0]: importAmount, + }) + defer func() { + if err := vm.Shutdown(); err != nil { + t.Fatal(err) + } + }() + + // issue a tx and build a Cortina block + vm.clock.Set(testCortinaTime) + importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*crypto.PrivateKeySECP256K1R{testKeys[0]}) + if err != nil { + t.Fatal(err) + } + if err := vm.issueTx(importTx, true /*=local*/); err != nil { + t.Fatal(err) + } + + <-issuer + + // calling Verify on blk will succeed, we use it as + // a starting point to make an invalid block. + blk, err := vm.BuildBlock() + if err != nil { + t.Fatal(err) + } + validEthBlk := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock + + // make a bad block by setting ExtraStateRoot to common.Hash{} + badHeader := validEthBlk.Header() + badHeader.ExtraStateRoot = common.Hash{} + ethBlkBad := types.NewBlock(badHeader, validEthBlk.Transactions(), validEthBlk.Uncles(), nil, trie.NewStackTrie(nil), validEthBlk.ExtData(), true) + + badBlk, err := vm.newBlock(ethBlkBad) + if err != nil { + t.Fatal(err) + } + err = badBlk.Verify() + assert.ErrorIs(t, err, errInvalidExtraStateRoot) + + // make a bad block by setting ExtraStateRoot to an incorrect hash + badHeader = validEthBlk.Header() + badHeader.ExtraStateRoot = common.BytesToHash([]byte("incorrect")) + ethBlkBad = types.NewBlock(badHeader, validEthBlk.Transactions(), validEthBlk.Uncles(), nil, trie.NewStackTrie(nil), validEthBlk.ExtData(), true) + + badBlk, err = vm.newBlock(ethBlkBad) + if err != nil { + t.Fatal(err) + } + err = badBlk.Verify() + assert.ErrorIs(t, err, errInvalidExtraStateRoot) + + // make a bad block by setting the timestamp before Cortina. + badHeader = validEthBlk.Header() + badHeader.Time = uint64(testCortinaTime.Add(-2 * time.Second).Unix()) + ethBlkBad = types.NewBlock(badHeader, validEthBlk.Transactions(), validEthBlk.Uncles(), nil, trie.NewStackTrie(nil), validEthBlk.ExtData(), true) + + badBlk, err = vm.newBlock(ethBlkBad) + if err != nil { + t.Fatal(err) + } + err = badBlk.Verify() + assert.ErrorIs(t, err, errInvalidExtraStateRoot) +} diff --git a/coreth/plugin/evm/vm_test.go b/coreth/plugin/evm/vm_test.go index f895a649..1bec4ccc 100644 --- a/coreth/plugin/evm/vm_test.go +++ b/coreth/plugin/evm/vm_test.go @@ -75,8 +75,14 @@ var ( genesisJSONApricotPhase3 = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" genesisJSONApricotPhase4 = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" genesisJSONApricotPhase5 = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" - genesisJSONBlueberry = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0,\"blueberryBlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" - genesisJSONLatest = genesisJSONBlueberry + + genesisJSONApricotPhasePre6 = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0,\"apricotPhasePre6BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + genesisJSONApricotPhase6 = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0,\"apricotPhasePre6BlockTimestamp\":0,\"apricotPhase6BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + genesisJSONApricotPhasePost6 = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0,\"apricotPhasePre6BlockTimestamp\":0,\"apricotPhase6BlockTimestamp\":0,\"apricotPhasePost6BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + + genesisJSONBanff = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0,\"apricotPhasePre6BlockTimestamp\":0,\"apricotPhase6BlockTimestamp\":0,\"apricotPhasePost6BlockTimestamp\":0,\"banffBlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + genesisJSONCortina = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0,\"apricotPhase3BlockTimestamp\":0,\"apricotPhase4BlockTimestamp\":0,\"apricotPhase5BlockTimestamp\":0,\"apricotPhasePre6BlockTimestamp\":0,\"apricotPhase6BlockTimestamp\":0,\"apricotPhasePost6BlockTimestamp\":0,\"banffBlockTimestamp\":0,\"cortinaBlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0100000000000000000000000000000000000000\":{\"code\":\"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033\",\"balance\":\"0x0\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + genesisJSONLatest = genesisJSONBanff // TODO: update to Cortina apricotRulesPhase0 = params.Rules{} apricotRulesPhase1 = params.Rules{IsApricotPhase1: true} @@ -84,7 +90,9 @@ var ( apricotRulesPhase3 = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true, IsApricotPhase3: true} apricotRulesPhase4 = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true, IsApricotPhase3: true, IsApricotPhase4: true} apricotRulesPhase5 = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true, IsApricotPhase3: true, IsApricotPhase4: true, IsApricotPhase5: true} - // blueberryRules = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true, IsApricotPhase3: true, IsApricotPhase4: true, IsApricotPhase5: true, IsBlueberry: true} + apricotRulesPhase6 = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true, IsApricotPhase3: true, IsApricotPhase4: true, IsApricotPhase5: true, IsApricotPhasePre6: true, IsApricotPhase6: true, IsApricotPhasePost6: true} + banffRules = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true, IsApricotPhase3: true, IsApricotPhase4: true, IsApricotPhase5: true, IsApricotPhasePre6: true, IsApricotPhase6: true, IsApricotPhasePost6: true, IsBanff: true} + // cortinaRules = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true, IsApricotPhase3: true, IsApricotPhase4: true, IsApricotPhase5: true, IsApricotPhasePre6: true, IsApricotPhase6: true, IsApricotPhasePost6: true, IsBanff: true, IsCortina: true} ) func init() { @@ -388,8 +396,28 @@ func TestVMUpgrades(t *testing.T) { expectedGasPrice: big.NewInt(0), }, { - name: "Blueberry", - genesis: genesisJSONBlueberry, + name: "Apricot Phase Pre 6", + genesis: genesisJSONApricotPhasePre6, + expectedGasPrice: big.NewInt(0), + }, + { + name: "Apricot Phase 6", + genesis: genesisJSONApricotPhase6, + expectedGasPrice: big.NewInt(0), + }, + { + name: "Apricot Phase Post 6", + genesis: genesisJSONApricotPhasePost6, + expectedGasPrice: big.NewInt(0), + }, + { + name: "Banff", + genesis: genesisJSONBanff, + expectedGasPrice: big.NewInt(0), + }, + { + name: "Cortina", + genesis: genesisJSONCortina, expectedGasPrice: big.NewInt(0), }, } @@ -1471,7 +1499,8 @@ func TestBonusBlocksTxs(t *testing.T) { t.Fatal(err) } - bonusBlocks.Add(blk.ID()) + // Make [blk] a bonus block. + vm.atomicBackend.(*atomicBackend).bonusBlocks = map[uint64]ids.ID{blk.Height(): blk.ID()} // Remove the UTXOs from shared memory, so that non-bonus blocks will fail verification if err := vm.ctx.SharedMemory.Apply(map[ids.ID]*atomic.Requests{vm.ctx.XChainID: {RemoveRequests: [][]byte{inputID[:]}}}); err != nil { @@ -2910,6 +2939,10 @@ func TestReissueAtomicTx(t *testing.T) { t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) } + if err := blkA.Verify(); err != nil { + t.Fatal(err) + } + if err := vm.SetPreference(blkA.ID()); err != nil { t.Fatal(err) } @@ -3778,10 +3811,8 @@ func TestExtraStateChangeAtomicGasLimitExceeded(t *testing.T) { } func TestGetAtomicRepositoryRepairHeights(t *testing.T) { - mainnetHeights := getAtomicRepositoryRepairHeights(params.AvalancheMainnetChainID) + mainnetHeights := getAtomicRepositoryRepairHeights(bonusBlockMainnetHeights, canonicalBlockMainnetHeights) assert.Len(t, mainnetHeights, 76) sorted := sort.SliceIsSorted(mainnetHeights, func(i, j int) bool { return mainnetHeights[i] < mainnetHeights[j] }) assert.True(t, sorted) - testnetHeights := getAtomicRepositoryRepairHeights(params.AvalancheFujiChainID) - assert.Empty(t, testnetHeights) } diff --git a/coreth/rpc/http.go b/coreth/rpc/http.go index a2c452bb..528fd8be 100644 --- a/coreth/rpc/http.go +++ b/coreth/rpc/http.go @@ -101,6 +101,14 @@ type HTTPTimeouts struct { // ReadHeaderTimeout. It is valid to use them both. ReadTimeout time.Duration + // ReadHeaderTimeout is the amount of time allowed to read + // request headers. The connection's read deadline is reset + // after reading the headers and the Handler can decide what + // is considered too slow for the body. If ReadHeaderTimeout + // is zero, the value of ReadTimeout is used. If both are + // zero, there is no timeout. + ReadHeaderTimeout time.Duration + // WriteTimeout is the maximum duration before timing out // writes of the response. It is reset whenever a new // request's header is read. Like ReadTimeout, it does not @@ -117,9 +125,10 @@ type HTTPTimeouts struct { // DefaultHTTPTimeouts represents the default timeout values used if further // configuration is not provided. var DefaultHTTPTimeouts = HTTPTimeouts{ - ReadTimeout: 30 * time.Second, - WriteTimeout: 30 * time.Second, - IdleTimeout: 120 * time.Second, + ReadTimeout: 30 * time.Second, + ReadHeaderTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 120 * time.Second, } // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP diff --git a/coreth/rpc/server.go b/coreth/rpc/server.go index 4e905458..fa79a8b5 100644 --- a/coreth/rpc/server.go +++ b/coreth/rpc/server.go @@ -181,7 +181,7 @@ type PeerInfo struct { // Address of client. This will usually contain the IP address and port. RemoteAddr string - // Addditional information for HTTP and WebSocket connections. + // Additional information for HTTP and WebSocket connections. HTTP struct { // Protocol version, i.e. "HTTP/1.1". This is not set for WebSocket. Version string diff --git a/coreth/scripts/geth-allowed-packages.txt b/coreth/scripts/geth-allowed-packages.txt new file mode 100644 index 00000000..95f53d07 --- /dev/null +++ b/coreth/scripts/geth-allowed-packages.txt @@ -0,0 +1,15 @@ +"github.com/ethereum/go-ethereum/cmd/utils" +"github.com/ethereum/go-ethereum/common" +"github.com/ethereum/go-ethereum/common/bitutil" +"github.com/ethereum/go-ethereum/common/compiler" +"github.com/ethereum/go-ethereum/common/hexutil" +"github.com/ethereum/go-ethereum/common/math" +"github.com/ethereum/go-ethereum/common/prque" +"github.com/ethereum/go-ethereum/core/asm" +"github.com/ethereum/go-ethereum/crypto" +"github.com/ethereum/go-ethereum/crypto/blake2b" +"github.com/ethereum/go-ethereum/crypto/bls12381" +"github.com/ethereum/go-ethereum/crypto/bn256" +"github.com/ethereum/go-ethereum/event" +"github.com/ethereum/go-ethereum/log" +"github.com/ethereum/go-ethereum/rlp" diff --git a/coreth/scripts/lint_allowed_geth_imports.sh b/coreth/scripts/lint_allowed_geth_imports.sh new file mode 100644 index 00000000..b6801485 --- /dev/null +++ b/coreth/scripts/lint_allowed_geth_imports.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +# Ensure that there are no geth imports that are not marked as explicitly allowed via ./scripts/geth-allowed-packages.txt +# 1. Recursively search through all go files for any lines that include a direct import from go-ethereum +# 2. Sort the unique results +# #. Print out the difference between the search results and the list of specified allowed package imports from geth. +extra_imports=$(grep -r --include='*.go' '"github.com/ethereum/go-ethereum/.*"' -o -h | sort -u | comm -23 - ./scripts/geth-allowed-packages.txt) +if [ ! -z "${extra_imports}" ]; then + echo "new go-ethereum imports should be added to ./scripts/geth-allowed-packages.txt to prevent accidental imports:" + echo "${extra_imports}" + exit 1 +fi diff --git a/coreth/scripts/versions.sh b/coreth/scripts/versions.sh index b117a285..c7d6945d 100644 --- a/coreth/scripts/versions.sh +++ b/coreth/scripts/versions.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Set up the versions to be used -coreth_version=${CORETH_VERSION:-'v0.8.16'} +coreth_version=${CORETH_VERSION:-'v0.11.0'} # Don't export them as they're used in the context of other calls -avalanche_version=${AVALANCHE_VERSION:-'v1.7.17'} +avalanche_version=${AVALANCHE_VERSION:-'v1.8.5'} diff --git a/coreth/sync/statesync/sync_test.go b/coreth/sync/statesync/sync_test.go index 885bfb40..13d58c0c 100644 --- a/coreth/sync/statesync/sync_test.go +++ b/coreth/sync/statesync/sync_test.go @@ -4,9 +4,11 @@ package statesync import ( + "bytes" "context" "errors" "math/rand" + "runtime/pprof" "sync/atomic" "testing" "time" @@ -97,24 +99,34 @@ func waitFor(t *testing.T, result <-chan error, expected error, timeout time.Dur t.Fatal("unexpected error waiting for sync result", err) } case <-time.After(timeout): + // print a stack trace to assist with debugging + // if the test times out. + var stackBuf bytes.Buffer + pprof.Lookup("goroutine").WriteTo(&stackBuf, 2) + t.Log(stackBuf.String()) + // fail the test t.Fatal("unexpected timeout waiting for sync result") } } func TestSimpleSyncCases(t *testing.T) { - clientErr := errors.New("dummy client error") + var ( + numAccounts = 250 + numAccountsSmall = 10 + clientErr = errors.New("dummy client error") + ) tests := map[string]syncTest{ "accounts": { prepareForTest: func(t *testing.T) (ethdb.Database, *trie.Database, common.Hash) { serverTrieDB := trie.NewDatabase(memorydb.New()) - root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, 1000, nil) + root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, nil) return memorydb.New(), serverTrieDB, root }, }, "accounts with code": { prepareForTest: func(t *testing.T) (ethdb.Database, *trie.Database, common.Hash) { serverTrieDB := trie.NewDatabase(memorydb.New()) - root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, 1000, func(t *testing.T, index int, account types.StateAccount) types.StateAccount { + root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount { if index%3 == 0 { codeBytes := make([]byte, 256) _, err := rand.Read(codeBytes) @@ -134,14 +146,14 @@ func TestSimpleSyncCases(t *testing.T) { "accounts with code and storage": { prepareForTest: func(t *testing.T) (ethdb.Database, *trie.Database, common.Hash) { serverTrieDB := trie.NewDatabase(memorydb.New()) - root := fillAccountsWithStorage(t, serverTrieDB, common.Hash{}, 1000) + root := fillAccountsWithStorage(t, serverTrieDB, common.Hash{}, numAccounts) return memorydb.New(), serverTrieDB, root }, }, "accounts with storage": { prepareForTest: func(t *testing.T) (ethdb.Database, *trie.Database, common.Hash) { serverTrieDB := trie.NewDatabase(memorydb.New()) - root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, 1000, func(t *testing.T, i int, account types.StateAccount) types.StateAccount { + root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, func(t *testing.T, i int, account types.StateAccount) types.StateAccount { if i%5 == 0 { account.Root, _, _ = trie.GenerateTrie(t, serverTrieDB, 16, common.HashLength) } @@ -154,14 +166,14 @@ func TestSimpleSyncCases(t *testing.T) { "accounts with overlapping storage": { prepareForTest: func(t *testing.T) (ethdb.Database, *trie.Database, common.Hash) { serverTrieDB := trie.NewDatabase(memorydb.New()) - root, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, 1000, 3) + root, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, numAccounts, 3) return memorydb.New(), serverTrieDB, root }, }, "failed to fetch leafs": { prepareForTest: func(t *testing.T) (ethdb.Database, *trie.Database, common.Hash) { serverTrieDB := trie.NewDatabase(memorydb.New()) - root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, 100, nil) + root, _ := trie.FillAccounts(t, serverTrieDB, common.Hash{}, numAccountsSmall, nil) return memorydb.New(), serverTrieDB, root }, GetLeafsIntercept: func(_ message.LeafsRequest, _ message.LeafsResponse) (message.LeafsResponse, error) { @@ -172,7 +184,7 @@ func TestSimpleSyncCases(t *testing.T) { "failed to fetch code": { prepareForTest: func(t *testing.T) (ethdb.Database, *trie.Database, common.Hash) { serverTrieDB := trie.NewDatabase(memorydb.New()) - root := fillAccountsWithStorage(t, serverTrieDB, common.Hash{}, 100) + root := fillAccountsWithStorage(t, serverTrieDB, common.Hash{}, numAccountsSmall) return memorydb.New(), serverTrieDB, root }, GetCodeIntercept: func(_ []common.Hash, _ [][]byte) ([][]byte, error) { diff --git a/coreth/tests/init.go b/coreth/tests/init.go index 6cd7d87b..6ae8d059 100644 --- a/coreth/tests/init.go +++ b/coreth/tests/init.go @@ -224,7 +224,7 @@ var Forks = map[string]*params.ChainConfig{ ApricotPhase4BlockTimestamp: big.NewInt(0), ApricotPhase5BlockTimestamp: big.NewInt(0), }, - "Blueberry": { + "Banff": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), EIP150Block: big.NewInt(0), @@ -239,7 +239,25 @@ var Forks = map[string]*params.ChainConfig{ ApricotPhase3BlockTimestamp: big.NewInt(0), ApricotPhase4BlockTimestamp: big.NewInt(0), ApricotPhase5BlockTimestamp: big.NewInt(0), - BlueberryBlockTimestamp: big.NewInt(0), + BanffBlockTimestamp: big.NewInt(0), + }, + "Cortina": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + ApricotPhase1BlockTimestamp: big.NewInt(0), + ApricotPhase2BlockTimestamp: big.NewInt(0), + ApricotPhase3BlockTimestamp: big.NewInt(0), + ApricotPhase4BlockTimestamp: big.NewInt(0), + ApricotPhase5BlockTimestamp: big.NewInt(0), + BanffBlockTimestamp: big.NewInt(0), + CortinaBlockTimestamp: big.NewInt(0), }, } diff --git a/coreth/trie/committer.go b/coreth/trie/committer.go index 21f93b7d..535f2cf0 100644 --- a/coreth/trie/committer.go +++ b/coreth/trie/committer.go @@ -27,61 +27,48 @@ package trie import ( - "errors" "fmt" - "sync" "github.com/ethereum/go-ethereum/common" ) -// committer is a type used for the trie Commit operation. A committer has some -// internal preallocated temp space, and also a callback that is invoked when -// leaves are committed. The leafs are passed through the `leafCh`, to allow -// some level of parallelism. -// By 'some level' of parallelism, it's still the case that all leaves will be -// processed sequentially - onleaf will never be called in parallel or out of order. -type committer struct { - onleaf LeafCallback +// leaf represents a trie leaf node +type leaf struct { + blob []byte // raw blob of leaf + parent common.Hash // the hash of parent node } -// committers live in a global sync.Pool -var committerPool = sync.Pool{ - New: func() interface{} { - return &committer{} - }, +// committer is the tool used for the trie Commit operation. The committer will +// capture all dirty nodes during the commit process and keep them cached in +// insertion order. +type committer struct { + nodes *NodeSet + collectLeaf bool } // newCommitter creates a new committer or picks one from the pool. -func newCommitter(onleaf LeafCallback) *committer { - committer := committerPool.Get().(*committer) - committer.onleaf = onleaf - return committer -} - -func returnCommitterToPool(h *committer) { - h.onleaf = nil - committerPool.Put(h) +func newCommitter(owner common.Hash, collectLeaf bool) *committer { + return &committer{ + nodes: NewNodeSet(owner), + collectLeaf: collectLeaf, + } } // Commit collapses a node down into a hash node and inserts it into the database -func (c *committer) Commit(n node, db *Database) (hashNode, int, error) { - if db == nil { - return nil, 0, errors.New("no db provided") - } - - h, committed, err := c.commit(nil, n, db) +func (c *committer) Commit(n node) (hashNode, *NodeSet, error) { + h, err := c.commit(nil, n) if err != nil { - return nil, 0, err + return nil, nil, err } - return h.(hashNode), committed, nil + return h.(hashNode), c.nodes, nil } // commit collapses a node down into a hash node and inserts it into the database -func (c *committer) commit(path []byte, n node, db *Database) (node, int, error) { +func (c *committer) commit(path []byte, n node) (node, error) { // if this path is clean, use available cached data hash, dirty := n.cache() if hash != nil && !dirty { - return hash, 0, nil + return hash, nil } // Commit children, then parent, and remove the dirty flag. switch cn := n.(type) { @@ -91,36 +78,35 @@ func (c *committer) commit(path []byte, n node, db *Database) (node, int, error) // If the child is fullNode, recursively commit, // otherwise it can only be hashNode or valueNode. - var childCommitted int if _, ok := cn.Val.(*fullNode); ok { - childV, committed, err := c.commit(append(path, cn.Key...), cn.Val, db) + childV, err := c.commit(append(path, cn.Key...), cn.Val) if err != nil { - return nil, 0, err + return nil, err } - collapsed.Val, childCommitted = childV, committed + collapsed.Val = childV } // The key needs to be copied, since we're delivering it to database collapsed.Key = hexToCompact(cn.Key) - hashedNode := c.store(path, collapsed, db) + hashedNode := c.store(path, collapsed) if hn, ok := hashedNode.(hashNode); ok { - return hn, childCommitted + 1, nil + return hn, nil } - return collapsed, childCommitted, nil + return collapsed, nil case *fullNode: - hashedKids, childCommitted, err := c.commitChildren(path, cn, db) + hashedKids, err := c.commitChildren(path, cn) if err != nil { - return nil, 0, err + return nil, err } collapsed := cn.copy() collapsed.Children = hashedKids - hashedNode := c.store(path, collapsed, db) + hashedNode := c.store(path, collapsed) if hn, ok := hashedNode.(hashNode); ok { - return hn, childCommitted + 1, nil + return hn, nil } - return collapsed, childCommitted, nil + return collapsed, nil case hashNode: - return cn, 0, nil + return cn, nil default: // nil, valuenode shouldn't be committed panic(fmt.Sprintf("%T: invalid node: %v", n, n)) @@ -128,11 +114,8 @@ func (c *committer) commit(path []byte, n node, db *Database) (node, int, error) } // commitChildren commits the children of the given fullnode -func (c *committer) commitChildren(path []byte, n *fullNode, db *Database) ([17]node, int, error) { - var ( - committed int - children [17]node - ) +func (c *committer) commitChildren(path []byte, n *fullNode) ([17]node, error) { + var children [17]node for i := 0; i < 16; i++ { child := n.Children[i] if child == nil { @@ -148,68 +131,62 @@ func (c *committer) commitChildren(path []byte, n *fullNode, db *Database) ([17] // Commit the child recursively and store the "hashed" value. // Note the returned node can be some embedded nodes, so it's // possible the type is not hashNode. - hashed, childCommitted, err := c.commit(append(path, byte(i)), child, db) + hashed, err := c.commit(append(path, byte(i)), child) if err != nil { - return children, 0, err + return children, err } children[i] = hashed - committed += childCommitted } // For the 17th child, it's possible the type is valuenode. if n.Children[16] != nil { children[16] = n.Children[16] } - return children, committed, nil + return children, nil } // store hashes the node n and if we have a storage layer specified, it writes // the key/value pair to it and tracks any node->child references as well as any // node->external trie references. -func (c *committer) store(path []byte, n node, db *Database) node { +func (c *committer) store(path []byte, n node) node { // Larger nodes are replaced by their hash and stored in the database. - var ( - hashNode, _ = n.cache() - size int - ) - if hashNode == nil { - // This was not generated - must be a small node stored in the parent. - // In theory, we should apply the leafCall here if it's not nil(embedded - // node usually contains value). But small value(less than 32bytes) is - // not our target. + var hash, _ = n.cache() + // This was not generated - must be a small node stored in the parent. + // In theory, we should check if the node is leaf here (embedded node + // usually is leaf node). But small value(less than 32bytes) is not + // our target(leaves in account trie only). + if hash == nil { return n - } else { - // We have the hash already, estimate the RLP encoding-size of the node. - // The size is used for mem tracking, does not need to be exact - size = estimateSize(n) } - - hash := common.BytesToHash(hashNode) - // Serially insert the nodes into the database - if db != nil { - db.insert(hash, size, n) - } - - // Invoke the leaf callback if present - if c.onleaf != nil { - switch n := n.(type) { - case *shortNode: - if child, ok := n.Val.(valueNode); ok { - c.onleaf(nil, nil, child, hash, nil) - } - case *fullNode: - // For children in range [0, 15], it's impossible - // to contain valueNode. Only check the 17th child. - if n.Children[16] != nil { - c.onleaf(nil, nil, n.Children[16].(valueNode), hash, nil) + // We have the hash already, estimate the RLP encoding-size of the node. + // The size is used for mem tracking, does not need to be exact + var ( + size = estimateSize(n) + nhash = common.BytesToHash(hash) + mnode = &memoryNode{ + hash: nhash, + node: simplifyNode(n), + size: uint16(size), + } + ) + // Collect the dirty node to nodeset for return. + c.nodes.add(string(path), mnode) + + // Collect the corresponding leaf node if it's required. We don't check + // full node since it's impossible to store value in fullNode. The key + // length of leaves should be exactly same. + if c.collectLeaf { + if sn, ok := n.(*shortNode); ok { + if val, ok := sn.Val.(valueNode); ok { + c.nodes.addLeaf(&leaf{blob: val, parent: nhash}) } } } - return hashNode + return hash } // estimateSize estimates the size of an rlp-encoded node, without actually // rlp-encoding it (zero allocs). This method has been experimentally tried, and with a trie -// with 1000 leafs, the only errors above 1% are on small shortnodes, where this +// with 1000 leaves, the only errors above 1% are on small shortnodes, where this // method overestimates by 2 or 3 bytes (e.g. 37 instead of 35) func estimateSize(n node) int { switch n := n.(type) { diff --git a/coreth/trie/database.go b/coreth/trie/database.go index 666c91e4..b98acd92 100644 --- a/coreth/trie/database.go +++ b/coreth/trie/database.go @@ -36,6 +36,7 @@ import ( "github.com/VictoriaMetrics/fastcache" "github.com/ava-labs/coreth/core/rawdb" + "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/coreth/ethdb" "github.com/ava-labs/coreth/metrics" "github.com/ethereum/go-ethereum/common" @@ -43,10 +44,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -const ( - defaultPreimagesLimit = 4 * 1024 * 1024 // 4 MB -) - var ( memcacheCleanHitMeter = metrics.NewRegisteredMeter("trie/memcache/clean/hit", nil) memcacheCleanMissMeter = metrics.NewRegisteredMeter("trie/memcache/clean/miss", nil) @@ -87,15 +84,10 @@ var ( type Database struct { diskdb ethdb.KeyValueStore // Persistent storage for matured trie nodes - preimagesLock sync.RWMutex // Used to gate acess to [preimagesSize] and [preimages] - preimagesSize common.StorageSize // Storage size of the preimages cache - preimages map[common.Hash][]byte // Preimages of nodes from the secure trie - - dirtiesLock sync.RWMutex // Used to gate access to all trie node data structures and metrics (everything below) - cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs - dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes - oldest common.Hash // Oldest tracked node, flush-list head - newest common.Hash // Newest tracked node, flush-list tail + cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs + dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes + oldest common.Hash // Oldest tracked node, flush-list head + newest common.Hash // Newest tracked node, flush-list tail gctime time.Duration // Time spent on garbage collection since last commit gcnodes uint64 // Nodes garbage collected since last commit @@ -107,6 +99,9 @@ type Database struct { dirtiesSize common.StorageSize // Storage size of the dirty node cache (exc. metadata) childrenSize common.StorageSize // Storage size of the external children tracking + preimages *preimageStore // The store for caching preimages + + lock sync.RWMutex } // rawNode is a simple binary blob used to differentiate between collapsed trie @@ -182,7 +177,10 @@ func (n *cachedNode) rlp() []byte { // or by regenerating it from the rlp encoded blob. func (n *cachedNode) obj(hash common.Hash) node { if node, ok := n.node.(rawNode); ok { - return mustDecodeNode(hash[:], node) + // The raw-blob format nodes are loaded either from the + // clean cache or the database, they are all in their own + // copy and safe to use unsafe decoder. + return mustDecodeNodeUnsafe(hash[:], node) } return expandNode(hash[:], n.node) } @@ -300,15 +298,17 @@ func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database if config != nil && config.Cache > 0 { cleans = fastcache.New(config.Cache * 1024 * 1024) } + var preimage *preimageStore + if config != nil && config.Preimages { + preimage = newPreimageStore(diskdb) + } db := &Database{ diskdb: diskdb, cleans: cleans, dirties: map[common.Hash]*cachedNode{{}: { children: make(map[common.Hash]uint16), }}, - } - if config == nil || config.Preimages { // TODO(karalabe): Flip to default off in the future - db.preimages = make(map[common.Hash][]byte) + preimages: preimage, } return db } @@ -318,11 +318,9 @@ func (db *Database) DiskDB() ethdb.KeyValueStore { return db.diskdb } -// insert inserts a collapsed trie node into the memory database. -// The blob size must be specified to allow proper size tracking. +// insert inserts a simplified trie node into the memory database. // All nodes inserted by this function will be reference tracked // and in theory should only used for **trie nodes** insertion. -// insert assumes that the dirtiesLock is held by the caller. func (db *Database) insert(hash common.Hash, size int, node node) { // If the node's already cached, skip if _, ok := db.dirties[hash]; ok { @@ -332,7 +330,7 @@ func (db *Database) insert(hash common.Hash, size int, node node) { // Create the cached entry for this node entry := &cachedNode{ - node: simplifyNode(node), + node: node, size: uint16(size), flushPrev: db.newest, } @@ -352,46 +350,6 @@ func (db *Database) insert(hash common.Hash, size int, node node) { db.dirtiesSize += common.StorageSize(common.HashLength + entry.size) } -// InsertPreimages writes a map of new trie node preimages to the -// memory database if it's yet unknown. -// -// The method will NOT make a copy of the provided slice, -// only use if the preimage will NOT be changed later on. -func (db *Database) InsertPreimages(preimages map[string][]byte) { - // Short circuit if preimage collection is disabled - if db.preimages == nil { - return - } - // Track the preimage if a yet unknown one - db.preimagesLock.Lock() - defer db.preimagesLock.Unlock() - for hk, preimage := range preimages { - hash := common.BytesToHash([]byte(hk)) - if _, ok := db.preimages[hash]; ok { - continue - } - db.preimages[hash] = preimage - db.preimagesSize += common.StorageSize(common.HashLength + len(preimage)) - } -} - -// Preimage retrieves a cached trie node pre-image from memory. If it cannot be -// found cached, the method queries the persistent database for the content. -func (db *Database) Preimage(hash common.Hash) []byte { - // Short circuit if preimage collection is disabled - if db.preimages == nil { - return nil - } - // Retrieve the node from cache if available - db.preimagesLock.RLock() - preimage := db.preimages[hash] - db.preimagesLock.RUnlock() - if preimage != nil { - return preimage - } - return rawdb.ReadPreimage(db.diskdb, hash) -} - // RawNode retrieves an encoded cached trie node from memory. If it cannot be found // cached, the method queries the persistent database for the content. This function // will not return the metaroot. @@ -437,9 +395,9 @@ func (db *Database) node(hash common.Hash) ([]byte, *cachedNode, error) { } } // Retrieve the node from the dirty cache if available - db.dirtiesLock.RLock() + db.lock.RLock() dirty := db.dirties[hash] - db.dirtiesLock.RUnlock() + db.lock.RUnlock() if dirty != nil { memcacheDirtyHitMeter.Mark(1) @@ -465,8 +423,8 @@ func (db *Database) node(hash common.Hash) ([]byte, *cachedNode, error) { // This method is extremely expensive and should only be used to validate internal // states in test code. func (db *Database) Nodes() []common.Hash { - db.dirtiesLock.RLock() - defer db.dirtiesLock.RUnlock() + db.lock.RLock() + defer db.lock.RUnlock() var hashes = make([]common.Hash, 0, len(db.dirties)) for hash := range db.dirties { @@ -481,12 +439,14 @@ func (db *Database) Nodes() []common.Hash { // This function is used to add reference between internal trie node // and external node(e.g. storage trie root), all internal trie nodes // are referenced together by database itself. -func (db *Database) Reference(child common.Hash, parent common.Hash, exclusive bool) { - if exclusive { - db.dirtiesLock.Lock() - defer db.dirtiesLock.Unlock() - } +func (db *Database) Reference(child common.Hash, parent common.Hash) { + db.lock.Lock() + defer db.lock.Unlock() + db.reference(child, parent) +} + +func (db *Database) reference(child common.Hash, parent common.Hash) { // If the node does not exist, it's a node pulled from disk, skip node, ok := db.dirties[child] if !ok { @@ -514,8 +474,8 @@ func (db *Database) Dereference(root common.Hash) { return } - db.dirtiesLock.Lock() - defer db.dirtiesLock.Unlock() + db.lock.Lock() + defer db.lock.Unlock() nodes, storage, start := len(db.dirties), db.dirtiesSize, time.Now() db.dereference(root, common.Hash{}) @@ -585,49 +545,6 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) { } } -// WritePreimages writes all preimages to disk if more than [limit] are currently in -// memory. -func (db *Database) WritePreimages(limit common.StorageSize) error { - // Short circuit if preimage collection is disabled - if db.preimages == nil { - return nil - } - - // If the preimage cache got large enough, push to disk. If it's still small - // leave for later to deduplicate writes. - db.preimagesLock.RLock() - if db.preimagesSize <= limit { - db.preimagesLock.RUnlock() - return nil - } - toFlush := make(map[common.Hash][]byte) - for k, v := range db.preimages { - toFlush[k] = v - } - db.preimagesLock.RUnlock() - - // Short circuit if nothing to do - if len(toFlush) == 0 { - return nil - } - - // Write preimages to disk - batch := db.diskdb.NewBatch() - rawdb.WritePreimages(batch, toFlush) - if err := batch.Write(); err != nil { - return err - } - - // Write successful, clear out the flushed data - db.preimagesLock.Lock() - defer db.preimagesLock.Unlock() - for hash, preimage := range toFlush { - delete(db.preimages, hash) - db.preimagesSize -= common.StorageSize(common.HashLength + len(preimage)) - } - return nil -} - // flushItem is used to track all [cachedNode]s that must be written to disk type flushItem struct { hash common.Hash @@ -668,15 +585,19 @@ func (db *Database) writeFlushItems(toFlush []flushItem) error { // memory usage goes below the given threshold. func (db *Database) Cap(limit common.StorageSize) error { start := time.Now() - if err := db.WritePreimages(defaultPreimagesLimit); err != nil { - return err + // If the preimage cache got large enough, push to disk. If it's still small + // leave for later to deduplicate writes. + if db.preimages != nil { + if err := db.preimages.commit(false); err != nil { + return err + } } // It is important that outside code doesn't see an inconsistent state // (referenced data removed from memory cache during commit but not yet // in persistent storage). This is ensured by only uncaching existing // data when the database write finalizes. - db.dirtiesLock.RLock() + db.lock.RLock() lockStart := time.Now() nodes, storage := len(db.dirties), db.dirtiesSize @@ -686,7 +607,7 @@ func (db *Database) Cap(limit common.StorageSize) error { pendingSize := db.dirtiesSize + common.StorageSize((len(db.dirties)-1)*cachedNodeSize) pendingSize += db.childrenSize - common.StorageSize(len(db.dirties[common.Hash{}].children)*(common.HashLength+2)) if pendingSize <= limit { - db.dirtiesLock.RUnlock() + db.lock.RUnlock() return nil } @@ -707,7 +628,7 @@ func (db *Database) Cap(limit common.StorageSize) error { } oldest = node.flushNext } - db.dirtiesLock.RUnlock() + db.lock.RUnlock() lockTime := time.Since(lockStart) // Write nodes to disk @@ -719,8 +640,8 @@ func (db *Database) Cap(limit common.StorageSize) error { // // NOTE: The order of the flushlist may have changed while the lock was not // held, so we cannot just iterate to [oldest]. - db.dirtiesLock.Lock() - defer db.dirtiesLock.Unlock() + db.lock.Lock() + defer db.lock.Unlock() lockStart = time.Now() for _, item := range toFlush { // [item.rlp] is populated in [writeFlushItems] @@ -750,14 +671,16 @@ func (db *Database) Cap(limit common.StorageSize) error { // effect, all pre-images accumulated up to this point are also written. func (db *Database) Commit(node common.Hash, report bool, callback func(common.Hash)) error { start := time.Now() - if err := db.WritePreimages(0); err != nil { - return err + if db.preimages != nil { + if err := db.preimages.commit(true); err != nil { + return err + } } // It is important that outside code doesn't see an inconsistent state (referenced // data removed from memory cache during commit but not yet in persistent storage). // This is ensured by only uncaching existing data when the database write finalizes. - db.dirtiesLock.RLock() + db.lock.RLock() lockStart := time.Now() nodes, storage := len(db.dirties), db.dirtiesSize toFlush, err := db.commit(node, make([]flushItem, 0, 128), callback) @@ -765,7 +688,7 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H log.Error("Failed to commit trie from trie database", "err", err) return err } - db.dirtiesLock.RUnlock() + db.lock.RUnlock() lockTime := time.Since(lockStart) // Write nodes to disk @@ -774,8 +697,8 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H } // Flush all written items from dirites - db.dirtiesLock.Lock() - defer db.dirtiesLock.Unlock() + db.lock.Lock() + defer db.lock.Unlock() lockStart = time.Now() for _, item := range toFlush { // [item.rlp] is populated in [writeFlushItems] @@ -877,19 +800,98 @@ func (db *Database) removeFromDirties(hash common.Hash, rlp []byte) { } } +// Update inserts the dirty nodes in provided nodeset into database and +// links the account trie with multiple storage tries if necessary. +func (db *Database) Update(nodes *MergedNodeSet) error { + db.lock.Lock() + defer db.lock.Unlock() + + return db.update(nodes) +} + +// UpdateAndReferenceRoot inserts the dirty nodes in provided nodeset into +// database and links the account trie with multiple storage tries if necessary, +// then adds a reference [from] root to the metaroot while holding the db's lock. +func (db *Database) UpdateAndReferenceRoot(nodes *MergedNodeSet, root common.Hash) error { + db.lock.Lock() + defer db.lock.Unlock() + + if err := db.update(nodes); err != nil { + return err + } + db.reference(root, common.Hash{}) + return nil +} + +func (db *Database) update(nodes *MergedNodeSet) error { + // Insert dirty nodes into the database. In the same tree, it must be + // ensured that children are inserted first, then parent so that children + // can be linked with their parent correctly. + // + // Note, the storage tries must be flushed before the account trie to + // retain the invariant that children go into the dirty cache first. + var order []common.Hash + for owner := range nodes.sets { + if owner == (common.Hash{}) { + continue + } + order = append(order, owner) + } + if _, ok := nodes.sets[common.Hash{}]; ok { + order = append(order, common.Hash{}) + } + for _, owner := range order { + subset := nodes.sets[owner] + for _, path := range subset.paths { + n, ok := subset.nodes[path] + if !ok { + return fmt.Errorf("missing node %x %v", owner, path) + } + db.insert(n.hash, int(n.size), n.node) + } + } + // Link up the account trie and storage trie if the node points + // to an account trie leaf. + if set, present := nodes.sets[common.Hash{}]; present { + for _, n := range set.leaves { + var account types.StateAccount + if err := rlp.DecodeBytes(n.blob, &account); err != nil { + return err + } + if account.Root != emptyRoot { + db.reference(account.Root, n.parent) + } + } + } + return nil +} + // Size returns the current storage size of the memory cache in front of the // persistent database layer. func (db *Database) Size() (common.StorageSize, common.StorageSize) { - db.preimagesLock.RLock() - preimagesSize := db.preimagesSize - db.preimagesLock.RUnlock() - // db.dirtiesSize only contains the useful data in the cache, but when reporting // the total memory consumption, the maintenance metadata is also needed to be // counted. - db.dirtiesLock.RLock() - defer db.dirtiesLock.RUnlock() + db.lock.RLock() + defer db.lock.RUnlock() var metadataSize = common.StorageSize((len(db.dirties) - 1) * cachedNodeSize) var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2)) - return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, preimagesSize + var preimageSize common.StorageSize + if db.preimages != nil { + preimageSize = db.preimages.size() + } + return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, preimageSize +} + +// CommitPreimages flushes the dangling preimages to disk. It is meant to be +// called when closing the blockchain object, so that preimages are persisted +// to the database. +func (db *Database) CommitPreimages() error { + db.lock.Lock() + defer db.lock.Unlock() + + if db.preimages == nil { + return nil + } + return db.preimages.commit(true) } diff --git a/coreth/trie/database_test.go b/coreth/trie/database_test.go index 3fe9d31e..3a943a8a 100644 --- a/coreth/trie/database_test.go +++ b/coreth/trie/database_test.go @@ -27,13 +27,10 @@ package trie import ( - "math/rand" "testing" - "time" "github.com/ava-labs/coreth/ethdb/memorydb" "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/assert" ) // Tests that the trie database returns a missing trie node error if attempting @@ -44,64 +41,3 @@ func TestDatabaseMetarootFetch(t *testing.T) { t.Fatalf("metaroot retrieval succeeded") } } - -// Tests that calling dereference does not interfere with a concurrent commit on -// a trie with the same underlying trieDB. -func TestDereferenceWhileCommit(t *testing.T) { - var ( - numKeys = 10 - keyLen = 10 - db = NewDatabase(memorydb.New()) - ) - - // set up a database with a small trie - tr1 := NewEmpty(db) - rand.Seed(1) // set random seed so we get deterministic key/values - FillTrie(t, numKeys, keyLen, tr1) - root, _, err := tr1.Commit(nil, false) - assert.NoError(t, err) - assert.NotZero(t, root) - db.Reference(root, common.Hash{}, true) - - // call Dereference from onleafs to simulate - // this occurring concurrently. - // the second trie has one more leaf so it - // does not share the same root as the first trie. - firstLeaf := true - tr2 := NewEmpty(db) - rand.Seed(1) // set random seed so we get deterministic key/values - FillTrie(t, numKeys+1, keyLen, tr2) - done := make(chan struct{}) - onleaf := func([][]byte, []byte, []byte, common.Hash, []byte) error { - if firstLeaf { - go func() { - db.Dereference(root) - close(done) - }() - select { - case <-done: - t.Fatal("Dereference succeeded within leaf callback") - case <-time.After(time.Second): - } - firstLeaf = false - } - return nil - } - root2, _, err := tr2.Commit(onleaf, false) - assert.NoError(t, err) - assert.NotEqual(t, root, root2) - db.Reference(root2, common.Hash{}, true) - - // wait for the goroutine to exit. - <-done - - // expected behavior is for root2 to - // be present and the trie should iterate - // without missing nodes. - tr3, err := New(common.Hash{}, root2, db) - assert.NoError(t, err) - it := tr3.NodeIterator(nil) - for it.Next(true) { - } - assert.NoError(t, it.Error()) -} diff --git a/coreth/trie/hasher.go b/coreth/trie/hasher.go index 5b6db8cd..69d4f6d6 100644 --- a/coreth/trie/hasher.go +++ b/coreth/trie/hasher.go @@ -201,7 +201,7 @@ func (h *hasher) hashData(data []byte) hashNode { } // proofHash is used to construct trie proofs, and returns the 'collapsed' -// node (for later RLP encoding) aswell as the hashed node -- unless the +// node (for later RLP encoding) as well as the hashed node -- unless the // node is smaller than 32 bytes, in which case it will be returned as is. // This method does not do anything on value- or hash-nodes. func (h *hasher) proofHash(original node) (collapsed, hashed node) { diff --git a/coreth/trie/iterator.go b/coreth/trie/iterator.go index 98f38ef7..8742a7a5 100644 --- a/coreth/trie/iterator.go +++ b/coreth/trie/iterator.go @@ -385,8 +385,7 @@ func (it *nodeIterator) resolveHash(hash hashNode, path []byte) (node, error) { } } } - resolved, err := it.trie.resolveHash(hash, path) - return resolved, err + return it.trie.resolveHash(hash, path) } func (it *nodeIterator) resolveBlob(hash hashNode, path []byte) ([]byte, error) { diff --git a/coreth/trie/iterator_test.go b/coreth/trie/iterator_test.go index ed032738..ac017cdf 100644 --- a/coreth/trie/iterator_test.go +++ b/coreth/trie/iterator_test.go @@ -41,7 +41,7 @@ import ( ) func TestEmptyIterator(t *testing.T) { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) iter := trie.NodeIterator(nil) seen := make(map[string]struct{}) @@ -54,7 +54,8 @@ func TestEmptyIterator(t *testing.T) { } func TestIterator(t *testing.T) { - trie := newEmpty() + db := NewDatabase(rawdb.NewMemoryDatabase()) + trie := NewEmpty(db) vals := []struct{ k, v string }{ {"do", "verb"}, {"ether", "wookiedoo"}, @@ -69,8 +70,13 @@ func TestIterator(t *testing.T) { all[val.k] = val.v trie.Update([]byte(val.k), []byte(val.v)) } - trie.Commit(nil, false) + root, nodes, err := trie.Commit(false) + if err != nil { + t.Fatalf("Failed to commit trie %v", err) + } + db.Update(NewWithNodeSet(nodes)) + trie, _ = New(common.Hash{}, root, db) found := make(map[string]string) it := NewIterator(trie.NodeIterator(nil)) for it.Next() { @@ -90,7 +96,7 @@ type kv struct { } func TestIteratorLargeData(t *testing.T) { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) vals := make(map[string]*kv) for i := byte(0); i < 255; i++ { @@ -183,7 +189,7 @@ var testdata2 = []kvs{ } func TestIteratorSeek(t *testing.T) { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) for _, val := range testdata1 { trie.Update([]byte(val.k), []byte(val.v)) } @@ -224,17 +230,23 @@ func checkIteratorOrder(want []kvs, it *Iterator) error { } func TestDifferenceIterator(t *testing.T) { - triea := newEmpty() + dba := NewDatabase(rawdb.NewMemoryDatabase()) + triea := NewEmpty(dba) for _, val := range testdata1 { triea.Update([]byte(val.k), []byte(val.v)) } - triea.Commit(nil, false) + rootA, nodesA, _ := triea.Commit(false) + dba.Update(NewWithNodeSet(nodesA)) + triea, _ = New(common.Hash{}, rootA, dba) - trieb := newEmpty() + dbb := NewDatabase(rawdb.NewMemoryDatabase()) + trieb := NewEmpty(dbb) for _, val := range testdata2 { trieb.Update([]byte(val.k), []byte(val.v)) } - trieb.Commit(nil, false) + rootB, nodesB, _ := trieb.Commit(false) + dbb.Update(NewWithNodeSet(nodesB)) + trieb, _ = New(common.Hash{}, rootB, dbb) found := make(map[string]string) di, _ := NewDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil)) @@ -260,17 +272,23 @@ func TestDifferenceIterator(t *testing.T) { } func TestUnionIterator(t *testing.T) { - triea := newEmpty() + dba := NewDatabase(rawdb.NewMemoryDatabase()) + triea := NewEmpty(dba) for _, val := range testdata1 { triea.Update([]byte(val.k), []byte(val.v)) } - triea.Commit(nil, false) + rootA, nodesA, _ := triea.Commit(false) + dba.Update(NewWithNodeSet(nodesA)) + triea, _ = New(common.Hash{}, rootA, dba) - trieb := newEmpty() + dbb := NewDatabase(rawdb.NewMemoryDatabase()) + trieb := NewEmpty(dbb) for _, val := range testdata2 { trieb.Update([]byte(val.k), []byte(val.v)) } - trieb.Commit(nil, false) + rootB, nodesB, _ := trieb.Commit(false) + dbb.Update(NewWithNodeSet(nodesB)) + trieb, _ = New(common.Hash{}, rootB, dbb) di, _ := NewUnionIterator([]NodeIterator{triea.NodeIterator(nil), trieb.NodeIterator(nil)}) it := NewIterator(di) @@ -326,7 +344,8 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) { for _, val := range testdata1 { tr.Update([]byte(val.k), []byte(val.v)) } - tr.Commit(nil, false) + _, nodes, _ := tr.Commit(false) + triedb.Update(NewWithNodeSet(nodes)) if !memonly { triedb.Commit(tr.Hash(), true, nil) } @@ -417,7 +436,8 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) { for _, val := range testdata1 { ctr.Update([]byte(val.k), []byte(val.v)) } - root, _, _ := ctr.Commit(nil, false) + root, nodes, _ := ctr.Commit(false) + triedb.Update(NewWithNodeSet(nodes)) if !memonly { triedb.Commit(root, true, nil) } @@ -516,11 +536,11 @@ func (l *loggingDb) Close() error { } // makeLargeTestTrie create a sample test trie -func makeLargeTestTrie() (*Database, *SecureTrie, *loggingDb) { +func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) { // Create an empty trie logDb := &loggingDb{0, memorydb.New()} triedb := NewDatabase(logDb) - trie, _ := NewSecure(common.Hash{}, common.Hash{}, triedb) + trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, triedb) // Fill it with some arbitrary data for i := 0; i < 10000; i++ { @@ -532,7 +552,8 @@ func makeLargeTestTrie() (*Database, *SecureTrie, *loggingDb) { val = crypto.Keccak256(val) trie.Update(key, val) } - trie.Commit(nil, false) + _, nodes, _ := trie.Commit(false) + triedb.Update(NewWithNodeSet(nodes)) // Return the generated trie return triedb, trie, logDb } @@ -571,7 +592,8 @@ func TestIteratorNodeBlob(t *testing.T) { all[val.k] = val.v trie.Update([]byte(val.k), []byte(val.v)) } - trie.Commit(nil, false) + _, nodes, _ := trie.Commit(false) + triedb.Update(NewWithNodeSet(nodes)) triedb.Cap(0) found := make(map[common.Hash][]byte) diff --git a/coreth/trie/node.go b/coreth/trie/node.go index dc50acd8..7b8dd73e 100644 --- a/coreth/trie/node.go +++ b/coreth/trie/node.go @@ -109,6 +109,7 @@ func (n valueNode) fstring(ind string) string { return fmt.Sprintf("%x ", []byte(n)) } +// mustDecodeNode is a wrapper of decodeNode and panic if any error is encountered. func mustDecodeNode(hash, buf []byte) node { n, err := decodeNode(hash, buf) if err != nil { @@ -117,8 +118,29 @@ func mustDecodeNode(hash, buf []byte) node { return n } -// decodeNode parses the RLP encoding of a trie node. +// mustDecodeNodeUnsafe is a wrapper of decodeNodeUnsafe and panic if any error is +// encountered. +func mustDecodeNodeUnsafe(hash, buf []byte) node { + n, err := decodeNodeUnsafe(hash, buf) + if err != nil { + panic(fmt.Sprintf("node %x: %v", hash, err)) + } + return n +} + +// decodeNode parses the RLP encoding of a trie node. It will deep-copy the passed +// byte slice for decoding, so it's safe to modify the byte slice afterwards. The- +// decode performance of this function is not optimal, but it is suitable for most +// scenarios with low performance requirements and hard to determine whether the +// byte slice be modified or not. func decodeNode(hash, buf []byte) (node, error) { + return decodeNodeUnsafe(hash, common.CopyBytes(buf)) +} + +// decodeNodeUnsafe parses the RLP encoding of a trie node. The passed byte slice +// will be directly referenced by node without bytes deep copy, so the input MUST +// not be changed after. +func decodeNodeUnsafe(hash, buf []byte) (node, error) { if len(buf) == 0 { return nil, io.ErrUnexpectedEOF } @@ -151,7 +173,7 @@ func decodeShort(hash, elems []byte) (node, error) { if err != nil { return nil, fmt.Errorf("invalid value node: %v", err) } - return &shortNode{key, append(valueNode{}, val...), flag}, nil + return &shortNode{key, valueNode(val), flag}, nil } r, _, err := decodeRef(rest) if err != nil { @@ -174,7 +196,7 @@ func decodeFull(hash, elems []byte) (*fullNode, error) { return n, err } if len(val) > 0 { - n.Children[16] = append(valueNode{}, val...) + n.Children[16] = valueNode(val) } return n, nil } @@ -200,7 +222,7 @@ func decodeRef(buf []byte) (node, []byte, error) { // empty node return nil, rest, nil case kind == rlp.String && len(val) == 32: - return append(hashNode{}, val...), rest, nil + return hashNode(val), rest, nil default: return nil, nil, fmt.Errorf("invalid RLP string size %d (want 0 or 32)", len(val)) } diff --git a/coreth/trie/node_test.go b/coreth/trie/node_test.go index f18aac4c..4671b798 100644 --- a/coreth/trie/node_test.go +++ b/coreth/trie/node_test.go @@ -30,6 +30,7 @@ import ( "bytes" "testing" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" ) @@ -102,3 +103,123 @@ func TestDecodeFullNode(t *testing.T) { t.Fatalf("decode full node err: %v", err) } } + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ava-labs/coreth/trie +// BenchmarkEncodeShortNode +// BenchmarkEncodeShortNode-8 16878850 70.81 ns/op 48 B/op 1 allocs/op +func BenchmarkEncodeShortNode(b *testing.B) { + node := &shortNode{ + Key: []byte{0x1, 0x2}, + Val: hashNode(randBytes(32)), + } + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + nodeToBytes(node) + } +} + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ava-labs/coreth/trie +// BenchmarkEncodeFullNode +// BenchmarkEncodeFullNode-8 4323273 284.4 ns/op 576 B/op 1 allocs/op +func BenchmarkEncodeFullNode(b *testing.B) { + node := &fullNode{} + for i := 0; i < 16; i++ { + node.Children[i] = hashNode(randBytes(32)) + } + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + nodeToBytes(node) + } +} + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ava-labs/coreth/trie +// BenchmarkDecodeShortNode +// BenchmarkDecodeShortNode-8 7925638 151.0 ns/op 157 B/op 4 allocs/op +func BenchmarkDecodeShortNode(b *testing.B) { + node := &shortNode{ + Key: []byte{0x1, 0x2}, + Val: hashNode(randBytes(32)), + } + blob := nodeToBytes(node) + hash := crypto.Keccak256(blob) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + mustDecodeNode(hash, blob) + } +} + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ava-labs/coreth/trie +// BenchmarkDecodeShortNodeUnsafe +// BenchmarkDecodeShortNodeUnsafe-8 9027476 128.6 ns/op 109 B/op 3 allocs/op +func BenchmarkDecodeShortNodeUnsafe(b *testing.B) { + node := &shortNode{ + Key: []byte{0x1, 0x2}, + Val: hashNode(randBytes(32)), + } + blob := nodeToBytes(node) + hash := crypto.Keccak256(blob) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + mustDecodeNodeUnsafe(hash, blob) + } +} + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ava-labs/coreth/trie +// BenchmarkDecodeFullNode +// BenchmarkDecodeFullNode-8 1597462 761.9 ns/op 1280 B/op 18 allocs/op +func BenchmarkDecodeFullNode(b *testing.B) { + node := &fullNode{} + for i := 0; i < 16; i++ { + node.Children[i] = hashNode(randBytes(32)) + } + blob := nodeToBytes(node) + hash := crypto.Keccak256(blob) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + mustDecodeNode(hash, blob) + } +} + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ava-labs/coreth/trie +// BenchmarkDecodeFullNodeUnsafe +// BenchmarkDecodeFullNodeUnsafe-8 1789070 687.1 ns/op 704 B/op 17 allocs/op +func BenchmarkDecodeFullNodeUnsafe(b *testing.B) { + node := &fullNode{} + for i := 0; i < 16; i++ { + node.Children[i] = hashNode(randBytes(32)) + } + blob := nodeToBytes(node) + hash := crypto.Keccak256(blob) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + mustDecodeNodeUnsafe(hash, blob) + } +} diff --git a/coreth/trie/nodeset.go b/coreth/trie/nodeset.go new file mode 100644 index 00000000..421ad134 --- /dev/null +++ b/coreth/trie/nodeset.go @@ -0,0 +1,104 @@ +// (c) 2022, Ava Labs, Inc. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" +) + +// memoryNode is all the information we know about a single cached trie node +// in the memory. +type memoryNode struct { + hash common.Hash // Node hash, computed by hashing rlp value + size uint16 // Byte size of the useful cached data + node node // Cached collapsed trie node, or raw rlp data +} + +// NodeSet contains all dirty nodes collected during the commit operation. +// Each node is keyed by path. It's not thread-safe to use. +type NodeSet struct { + owner common.Hash // the identifier of the trie + paths []string // the path of dirty nodes, sort by insertion order + nodes map[string]*memoryNode // the map of dirty nodes, keyed by node path + leaves []*leaf // the list of dirty leaves +} + +// NewNodeSet initializes an empty node set to be used for tracking dirty nodes +// from a specific account or storage trie. The owner is zero for the account +// trie and the owning account address hash for storage tries. +func NewNodeSet(owner common.Hash) *NodeSet { + return &NodeSet{ + owner: owner, + nodes: make(map[string]*memoryNode), + } +} + +// add caches node with provided path and node object. +func (set *NodeSet) add(path string, node *memoryNode) { + set.paths = append(set.paths, path) + set.nodes[path] = node +} + +// addLeaf caches the provided leaf node. +func (set *NodeSet) addLeaf(node *leaf) { + set.leaves = append(set.leaves, node) +} + +// Len returns the number of dirty nodes contained in the set. +func (set *NodeSet) Len() int { + return len(set.nodes) +} + +// MergedNodeSet represents a merged dirty node set for a group of tries. +type MergedNodeSet struct { + sets map[common.Hash]*NodeSet +} + +// NewMergedNodeSet initializes an empty merged set. +func NewMergedNodeSet() *MergedNodeSet { + return &MergedNodeSet{sets: make(map[common.Hash]*NodeSet)} +} + +// NewWithNodeSet constructs a merged nodeset with the provided single set. +func NewWithNodeSet(set *NodeSet) *MergedNodeSet { + merged := NewMergedNodeSet() + merged.Merge(set) + return merged +} + +// Merge merges the provided dirty nodes of a trie into the set. The assumption +// is held that no duplicated set belonging to the same trie will be merged twice. +func (set *MergedNodeSet) Merge(other *NodeSet) error { + _, present := set.sets[other.owner] + if present { + return fmt.Errorf("duplicate trie for owner %#x", other.owner) + } + set.sets[other.owner] = other + return nil +} diff --git a/coreth/trie/preimages.go b/coreth/trie/preimages.go new file mode 100644 index 00000000..4a11059e --- /dev/null +++ b/coreth/trie/preimages.go @@ -0,0 +1,107 @@ +// (c) 2022, Ava Labs, Inc. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "sync" + + "github.com/ava-labs/coreth/core/rawdb" + "github.com/ava-labs/coreth/ethdb" + "github.com/ethereum/go-ethereum/common" +) + +const defaultPreimagesLimit = 4 * 1024 * 1024 // 4 MB + +// preimageStore is the store for caching preimages of node key. +type preimageStore struct { + lock sync.RWMutex + disk ethdb.KeyValueStore + preimages map[common.Hash][]byte // Preimages of nodes from the secure trie + preimagesSize common.StorageSize // Storage size of the preimages cache +} + +// newPreimageStore initializes the store for caching preimages. +func newPreimageStore(disk ethdb.KeyValueStore) *preimageStore { + return &preimageStore{ + disk: disk, + preimages: make(map[common.Hash][]byte), + } +} + +// insertPreimage writes a new trie node pre-image to the memory database if it's +// yet unknown. The method will NOT make a copy of the slice, only use if the +// preimage will NOT be changed later on. +func (store *preimageStore) insertPreimage(preimages map[common.Hash][]byte) { + store.lock.Lock() + defer store.lock.Unlock() + + for hash, preimage := range preimages { + if _, ok := store.preimages[hash]; ok { + continue + } + store.preimages[hash] = preimage + store.preimagesSize += common.StorageSize(common.HashLength + len(preimage)) + } +} + +// preimage retrieves a cached trie node pre-image from memory. If it cannot be +// found cached, the method queries the persistent database for the content. +func (store *preimageStore) preimage(hash common.Hash) []byte { + store.lock.RLock() + preimage := store.preimages[hash] + store.lock.RUnlock() + + if preimage != nil { + return preimage + } + return rawdb.ReadPreimage(store.disk, hash) +} + +// commit flushes the cached preimages into the disk. +func (store *preimageStore) commit(force bool) error { + store.lock.Lock() + defer store.lock.Unlock() + + if store.preimagesSize <= defaultPreimagesLimit && !force { + return nil + } + batch := store.disk.NewBatch() + rawdb.WritePreimages(batch, store.preimages) + if err := batch.Write(); err != nil { + return err + } + store.preimages, store.preimagesSize = make(map[common.Hash][]byte), 0 + return nil +} + +// size returns the current storage size of accumulated preimages. +func (store *preimageStore) size() common.StorageSize { + store.lock.RLock() + defer store.lock.RUnlock() + + return store.preimagesSize +} diff --git a/coreth/trie/proof.go b/coreth/trie/proof.go index e5d81bff..329b9afe 100644 --- a/coreth/trie/proof.go +++ b/coreth/trie/proof.go @@ -31,6 +31,7 @@ import ( "errors" "fmt" + "github.com/ava-labs/coreth/core/rawdb" "github.com/ava-labs/coreth/ethdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -45,9 +46,12 @@ import ( // with the node that proves the absence of the key. func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error { // Collect all nodes on the path to key. + var ( + prefix []byte + nodes []node + tn = t.root + ) key = keybytesToHex(key) - var nodes []node - tn := t.root for len(key) > 0 && tn != nil { switch n := tn.(type) { case *shortNode: @@ -56,16 +60,18 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) e tn = nil } else { tn = n.Val + prefix = append(prefix, n.Key...) key = key[len(n.Key):] } nodes = append(nodes, n) case *fullNode: tn = n.Children[key[0]] + prefix = append(prefix, key[0]) key = key[1:] nodes = append(nodes, n) case hashNode: var err error - tn, err = t.resolveHash(n, nil) + tn, err = t.resolveHash(n, prefix) if err != nil { log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) return err @@ -104,7 +110,7 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) e // If the trie does not contain a value for key, the returned proof contains all // nodes of the longest existing prefix of the key (at least the root node), ending // with the node that proves the absence of the key. -func (t *SecureTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error { +func (t *StateTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error { return t.trie.Prove(key, fromLevel, proofDb) } @@ -563,7 +569,7 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key } // Rebuild the trie with the leaf stream, the shape of trie // should be same with the original one. - tr := newWithRootNode(root) + tr := &Trie{root: root, db: NewDatabase(rawdb.NewMemoryDatabase())} if empty { tr.root = nil } diff --git a/coreth/trie/proof_test.go b/coreth/trie/proof_test.go index d56667f3..4875dfeb 100644 --- a/coreth/trie/proof_test.go +++ b/coreth/trie/proof_test.go @@ -215,7 +215,7 @@ func TestRangeProofWithNonExistentProof(t *testing.T) { proof := memorydb.New() // Short circuit if the decreased key is same with the previous key - first := decreseKey(common.CopyBytes(entries[start].k)) + first := decreaseKey(common.CopyBytes(entries[start].k)) if start != 0 && bytes.Equal(first, entries[start-1].k) { continue } @@ -224,7 +224,7 @@ func TestRangeProofWithNonExistentProof(t *testing.T) { continue } // Short circuit if the increased key is same with the next key - last := increseKey(common.CopyBytes(entries[end-1].k)) + last := increaseKey(common.CopyBytes(entries[end-1].k)) if end != len(entries) && bytes.Equal(last, entries[end].k) { continue } @@ -284,7 +284,7 @@ func TestRangeProofWithInvalidNonExistentProof(t *testing.T) { // Case 1 start, end := 100, 200 - first := decreseKey(common.CopyBytes(entries[start].k)) + first := decreaseKey(common.CopyBytes(entries[start].k)) proof := memorydb.New() if err := trie.Prove(first, 0, proof); err != nil { @@ -307,7 +307,7 @@ func TestRangeProofWithInvalidNonExistentProof(t *testing.T) { // Case 2 start, end = 100, 200 - last := increseKey(common.CopyBytes(entries[end-1].k)) + last := increaseKey(common.CopyBytes(entries[end-1].k)) proof = memorydb.New() if err := trie.Prove(entries[start].k, 0, proof); err != nil { t.Fatalf("Failed to prove the first node %v", err) @@ -353,7 +353,7 @@ func TestOneElementRangeProof(t *testing.T) { // One element with left non-existent edge proof start = 1000 - first := decreseKey(common.CopyBytes(entries[start].k)) + first := decreaseKey(common.CopyBytes(entries[start].k)) proof = memorydb.New() if err := trie.Prove(first, 0, proof); err != nil { t.Fatalf("Failed to prove the first node %v", err) @@ -368,7 +368,7 @@ func TestOneElementRangeProof(t *testing.T) { // One element with right non-existent edge proof start = 1000 - last := increseKey(common.CopyBytes(entries[start].k)) + last := increaseKey(common.CopyBytes(entries[start].k)) proof = memorydb.New() if err := trie.Prove(entries[start].k, 0, proof); err != nil { t.Fatalf("Failed to prove the first node %v", err) @@ -383,7 +383,7 @@ func TestOneElementRangeProof(t *testing.T) { // One element with two non-existent edge proofs start = 1000 - first, last = decreseKey(common.CopyBytes(entries[start].k)), increseKey(common.CopyBytes(entries[start].k)) + first, last = decreaseKey(common.CopyBytes(entries[start].k)), increaseKey(common.CopyBytes(entries[start].k)) proof = memorydb.New() if err := trie.Prove(first, 0, proof); err != nil { t.Fatalf("Failed to prove the first node %v", err) @@ -651,9 +651,9 @@ func TestSameSideProofs(t *testing.T) { sort.Sort(entries) pos := 1000 - first := decreseKey(common.CopyBytes(entries[pos].k)) - first = decreseKey(first) - last := decreseKey(common.CopyBytes(entries[pos].k)) + first := decreaseKey(common.CopyBytes(entries[pos].k)) + first = decreaseKey(first) + last := decreaseKey(common.CopyBytes(entries[pos].k)) proof := memorydb.New() if err := trie.Prove(first, 0, proof); err != nil { @@ -667,9 +667,9 @@ func TestSameSideProofs(t *testing.T) { t.Fatalf("Expected error, got nil") } - first = increseKey(common.CopyBytes(entries[pos].k)) - last = increseKey(common.CopyBytes(entries[pos].k)) - last = increseKey(last) + first = increaseKey(common.CopyBytes(entries[pos].k)) + last = increaseKey(common.CopyBytes(entries[pos].k)) + last = increaseKey(last) proof = memorydb.New() if err := trie.Prove(first, 0, proof); err != nil { @@ -775,7 +775,7 @@ func TestEmptyRangeProof(t *testing.T) { } for _, c := range cases { proof := memorydb.New() - first := increseKey(common.CopyBytes(entries[c.pos].k)) + first := increaseKey(common.CopyBytes(entries[c.pos].k)) if err := trie.Prove(first, 0, proof); err != nil { t.Fatalf("Failed to prove the first node %v", err) } @@ -914,7 +914,7 @@ func mutateByte(b []byte) { } } -func increseKey(key []byte) []byte { +func increaseKey(key []byte) []byte { for i := len(key) - 1; i >= 0; i-- { key[i]++ if key[i] != 0x0 { @@ -924,7 +924,7 @@ func increseKey(key []byte) []byte { return key } -func decreseKey(key []byte) []byte { +func decreaseKey(key []byte) []byte { for i := len(key) - 1; i >= 0; i-- { key[i]-- if key[i] != 0xff { diff --git a/coreth/trie/secure_trie.go b/coreth/trie/secure_trie.go index 20c1afa4..68bcf22b 100644 --- a/coreth/trie/secure_trie.go +++ b/coreth/trie/secure_trie.go @@ -35,24 +35,35 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -// SecureTrie wraps a trie with key hashing. In a secure trie, all +// SecureTrie is the old name of StateTrie. +// Deprecated: use StateTrie. +type SecureTrie = StateTrie + +// NewSecure creates a new StateTrie. +// Deprecated: use NewStateTrie. +func NewSecure(owner common.Hash, root common.Hash, db *Database) (*SecureTrie, error) { + return NewStateTrie(owner, root, db) +} + +// StateTrie wraps a trie with key hashing. In a secure trie, all // access operations hash the key using keccak256. This prevents // calling code from creating long chains of nodes that // increase the access time. // -// Contrary to a regular trie, a SecureTrie can only be created with +// Contrary to a regular trie, a StateTrie can only be created with // New and must have an attached database. The database also stores // the preimage of each key. // -// SecureTrie is not safe for concurrent use. -type SecureTrie struct { +// StateTrie is not safe for concurrent use. +type StateTrie struct { trie Trie + preimages *preimageStore hashKeyBuf [common.HashLength]byte secKeyCache map[string][]byte - secKeyCacheOwner *SecureTrie // Pointer to self, replace the key cache on mismatch + secKeyCacheOwner *StateTrie // Pointer to self, replace the key cache on mismatch } -// NewSecure creates a trie with an existing root node from a backing database +// NewStateTrie creates a trie with an existing root node from a backing database // and optional intermediate in-memory node pool. // // If root is the zero hash or the sha3 hash of an empty string, the @@ -63,7 +74,7 @@ type SecureTrie struct { // Loaded nodes are kept around until their 'cache generation' expires. // A new cache generation is created by each call to Commit. // cachelimit sets the number of past cache generations to keep. -func NewSecure(owner common.Hash, root common.Hash, db *Database) (*SecureTrie, error) { +func NewStateTrie(owner common.Hash, root common.Hash, db *Database) (*StateTrie, error) { if db == nil { panic("trie.NewSecure called without a database") } @@ -71,12 +82,12 @@ func NewSecure(owner common.Hash, root common.Hash, db *Database) (*SecureTrie, if err != nil { return nil, err } - return &SecureTrie{trie: *trie}, nil + return &StateTrie{trie: *trie, preimages: db.preimages}, nil } // Get returns the value for key stored in the trie. // The value bytes must not be modified by the caller. -func (t *SecureTrie) Get(key []byte) []byte { +func (t *StateTrie) Get(key []byte) []byte { res, err := t.TryGet(key) if err != nil { log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) @@ -87,19 +98,50 @@ func (t *SecureTrie) Get(key []byte) []byte { // TryGet returns the value for key stored in the trie. // The value bytes must not be modified by the caller. // If a node was not found in the database, a MissingNodeError is returned. -func (t *SecureTrie) TryGet(key []byte) ([]byte, error) { +func (t *StateTrie) TryGet(key []byte) ([]byte, error) { return t.trie.TryGet(t.hashKey(key)) } +func (t *StateTrie) TryGetAccount(key []byte) (*types.StateAccount, error) { + var ret types.StateAccount + res, err := t.trie.TryGet(t.hashKey(key)) + if err != nil { + log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) + return &ret, err + } + if res == nil { + return nil, nil + } + err = rlp.DecodeBytes(res, &ret) + return &ret, err +} + +// TryGetAccountWithPreHashedKey does the same thing as TryGetAccount, however +// it expects a key that is already hashed. This constitutes an abstraction leak, +// since the client code needs to know the key format. +func (t *StateTrie) TryGetAccountWithPreHashedKey(key []byte) (*types.StateAccount, error) { + var ret types.StateAccount + res, err := t.trie.TryGet(key) + if err != nil { + log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) + return &ret, err + } + if res == nil { + return nil, nil + } + err = rlp.DecodeBytes(res, &ret) + return &ret, err +} + // TryGetNode attempts to retrieve a trie node by compact-encoded path. It is not // possible to use keybyte-encoding as the path might contain odd nibbles. -func (t *SecureTrie) TryGetNode(path []byte) ([]byte, int, error) { +func (t *StateTrie) TryGetNode(path []byte) ([]byte, int, error) { return t.trie.TryGetNode(path) } // TryUpdateAccount account will abstract the write of an account to the // secure trie. -func (t *SecureTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error { +func (t *StateTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error { hk := t.hashKey(key) data, err := rlp.EncodeToBytes(acc) if err != nil { @@ -118,7 +160,7 @@ func (t *SecureTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error // // The value bytes must not be modified by the caller while they are // stored in the trie. -func (t *SecureTrie) Update(key, value []byte) { +func (t *StateTrie) Update(key, value []byte) { if err := t.TryUpdate(key, value); err != nil { log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) } @@ -132,7 +174,7 @@ func (t *SecureTrie) Update(key, value []byte) { // stored in the trie. // // If a node was not found in the database, a MissingNodeError is returned. -func (t *SecureTrie) TryUpdate(key, value []byte) error { +func (t *StateTrie) TryUpdate(key, value []byte) error { hk := t.hashKey(key) err := t.trie.TryUpdate(hk, value) if err != nil { @@ -143,7 +185,7 @@ func (t *SecureTrie) TryUpdate(key, value []byte) error { } // Delete removes any existing value for key from the trie. -func (t *SecureTrie) Delete(key []byte) { +func (t *StateTrie) Delete(key []byte) { if err := t.TryDelete(key); err != nil { log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) } @@ -151,7 +193,14 @@ func (t *SecureTrie) Delete(key []byte) { // TryDelete removes any existing value for key from the trie. // If a node was not found in the database, a MissingNodeError is returned. -func (t *SecureTrie) TryDelete(key []byte) error { +func (t *StateTrie) TryDelete(key []byte) error { + hk := t.hashKey(key) + delete(t.getSecKeyCache(), string(hk)) + return t.trie.TryDelete(hk) +} + +// TryDeleteAccount abstracts an account deletion from the trie. +func (t *StateTrie) TryDeleteAccount(key []byte) error { hk := t.hashKey(key) delete(t.getSecKeyCache(), string(hk)) return t.trie.TryDelete(hk) @@ -159,52 +208,64 @@ func (t *SecureTrie) TryDelete(key []byte) error { // GetKey returns the sha3 preimage of a hashed key that was // previously used to store a value. -func (t *SecureTrie) GetKey(shaKey []byte) []byte { +func (t *StateTrie) GetKey(shaKey []byte) []byte { if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { return key } - return t.trie.db.Preimage(common.BytesToHash(shaKey)) + if t.preimages == nil { + return nil + } + return t.preimages.preimage(common.BytesToHash(shaKey)) } -// Commit writes all nodes and the secure hash pre-images to the trie's database. -// Nodes are stored with their sha3 hash as the key. -// -// Committing flushes nodes from memory. Subsequent Get calls will load nodes -// from the database. -func (t *SecureTrie) Commit(onleaf LeafCallback, referenceRoot bool) (common.Hash, int, error) { +// Commit collects all dirty nodes in the trie and replace them with the +// corresponding node hash. All collected nodes(including dirty leaves if +// collectLeaf is true) will be encapsulated into a nodeset for return. +// The returned nodeset can be nil if the trie is clean(nothing to commit). +// All cached preimages will be also flushed if preimages recording is enabled. +// Once the trie is committed, it's not usable anymore. A new trie must +// be created with new root and updated trie database for following usage +func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *NodeSet, error) { // Write all the pre-images to the actual disk database if len(t.getSecKeyCache()) > 0 { - t.trie.db.InsertPreimages(t.secKeyCache) // if preimages are disabled, this returns immediately + if t.preimages != nil { + preimages := make(map[common.Hash][]byte) + for hk, key := range t.secKeyCache { + preimages[common.BytesToHash([]byte(hk))] = key + } + t.preimages.insertPreimage(preimages) + } t.secKeyCache = make(map[string][]byte) } // Commit the trie to its intermediate node database - return t.trie.Commit(onleaf, referenceRoot) + return t.trie.Commit(collectLeaf) } // Hash returns the root hash of SecureTrie. It does not write to the // database and can be used even if the trie doesn't have one. -func (t *SecureTrie) Hash() common.Hash { +func (t *StateTrie) Hash() common.Hash { return t.trie.Hash() } // Copy returns a copy of SecureTrie. -func (t *SecureTrie) Copy() *SecureTrie { - return &SecureTrie{ +func (t *StateTrie) Copy() *StateTrie { + return &StateTrie{ trie: *t.trie.Copy(), + preimages: t.preimages, secKeyCache: t.secKeyCache, } } // NodeIterator returns an iterator that returns nodes of the underlying trie. Iteration // starts at the key after the given start key. -func (t *SecureTrie) NodeIterator(start []byte) NodeIterator { +func (t *StateTrie) NodeIterator(start []byte) NodeIterator { return t.trie.NodeIterator(start) } // hashKey returns the hash of key as an ephemeral buffer. // The caller must not hold onto the return value because it will become // invalid on the next call to hashKey or secKey. -func (t *SecureTrie) hashKey(key []byte) []byte { +func (t *StateTrie) hashKey(key []byte) []byte { h := newHasher(false) h.sha.Reset() h.sha.Write(key) @@ -216,7 +277,7 @@ func (t *SecureTrie) hashKey(key []byte) []byte { // getSecKeyCache returns the current secure key cache, creating a new one if // ownership changed (i.e. the current secure trie is a copy of another owning // the actual cache). -func (t *SecureTrie) getSecKeyCache() map[string][]byte { +func (t *StateTrie) getSecKeyCache() map[string][]byte { if t != t.secKeyCacheOwner { t.secKeyCacheOwner = t t.secKeyCache = make(map[string][]byte) diff --git a/coreth/trie/secure_trie_test.go b/coreth/trie/secure_trie_test.go index 39aaf854..a0821927 100644 --- a/coreth/trie/secure_trie_test.go +++ b/coreth/trie/secure_trie_test.go @@ -28,6 +28,7 @@ package trie import ( "bytes" + "fmt" "runtime" "sync" "testing" @@ -37,16 +38,16 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -func newEmptySecure() *SecureTrie { - trie, _ := NewSecure(common.Hash{}, common.Hash{}, NewDatabase(memorydb.New())) +func newEmptySecure() *StateTrie { + trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, NewDatabase(memorydb.New())) return trie } -// makeTestSecureTrie creates a large enough secure trie for testing. -func makeTestSecureTrie() (*Database, *SecureTrie, map[string][]byte) { +// makeTestStateTrie creates a large enough secure trie for testing. +func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) { // Create an empty trie triedb := NewDatabase(memorydb.New()) - trie, _ := NewSecure(common.Hash{}, common.Hash{}, triedb) + trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, triedb) // Fill it with some arbitrary data content := make(map[string][]byte) @@ -67,9 +68,15 @@ func makeTestSecureTrie() (*Database, *SecureTrie, map[string][]byte) { trie.Update(key, val) } } - trie.Commit(nil, false) - - // Return the generated trie + root, nodes, err := trie.Commit(false) + if err != nil { + panic(fmt.Errorf("failed to commit trie %v", err)) + } + if err := triedb.Update(NewWithNodeSet(nodes)); err != nil { + panic(fmt.Errorf("failed to commit db %v", err)) + } + // Re-create the trie based on the new state + trie, _ = NewSecure(common.Hash{}, root, triedb) return triedb, trie, content } @@ -115,12 +122,12 @@ func TestSecureGetKey(t *testing.T) { } } -func TestSecureTrieConcurrency(t *testing.T) { +func TestStateTrieConcurrency(t *testing.T) { // Create an initial trie and copy if for concurrent access - _, trie, _ := makeTestSecureTrie() + _, trie, _ := makeTestStateTrie() threads := runtime.NumCPU() - tries := make([]*SecureTrie, threads) + tries := make([]*StateTrie, threads) for i := 0; i < threads; i++ { tries[i] = trie.Copy() } @@ -145,7 +152,7 @@ func TestSecureTrieConcurrency(t *testing.T) { tries[index].Update(key, val) } } - tries[index].Commit(nil, false) + tries[index].Commit(false) }(i) } // Wait for all threads to finish diff --git a/coreth/trie/sync_test.go b/coreth/trie/sync_test.go index 64eb3578..c04a928c 100644 --- a/coreth/trie/sync_test.go +++ b/coreth/trie/sync_test.go @@ -27,15 +27,17 @@ package trie import ( + "fmt" + "github.com/ava-labs/coreth/ethdb/memorydb" "github.com/ethereum/go-ethereum/common" ) // makeTestTrie create a sample test trie to test node-wise reconstruction. -func makeTestTrie() (*Database, *SecureTrie, map[string][]byte) { +func makeTestTrie() (*Database, *StateTrie, map[string][]byte) { // Create an empty trie triedb := NewDatabase(memorydb.New()) - trie, _ := NewSecure(common.Hash{}, common.Hash{}, triedb) + trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, triedb) // Fill it with some arbitrary data content := make(map[string][]byte) @@ -56,8 +58,14 @@ func makeTestTrie() (*Database, *SecureTrie, map[string][]byte) { trie.Update(key, val) } } - trie.Commit(nil, false) - - // Return the generated trie + root, nodes, err := trie.Commit(false) + if err != nil { + panic(fmt.Errorf("failed to commit trie %v", err)) + } + if err := triedb.Update(NewWithNodeSet(nodes)); err != nil { + panic(fmt.Errorf("failed to commit db %v", err)) + } + // Re-create the trie based on the new state + trie, _ = NewSecure(common.Hash{}, root, triedb) return triedb, trie, content } diff --git a/coreth/trie/test_trie.go b/coreth/trie/test_trie.go index 4106b968..4013d92a 100644 --- a/coreth/trie/test_trie.go +++ b/coreth/trie/test_trie.go @@ -32,7 +32,9 @@ func GenerateTrie(t *testing.T, trieDB *Database, numKeys int, keySize int) (com keys, values := FillTrie(t, numKeys, keySize, testTrie) // Commit the root to [trieDB] - root, _, err := testTrie.Commit(nil, false) + root, nodes, err := testTrie.Commit(false) + assert.NoError(t, err) + err = trieDB.Update(NewWithNodeSet(nodes)) assert.NoError(t, err) err = trieDB.Commit(root, false, nil) assert.NoError(t, err) @@ -143,7 +145,7 @@ func FillAccounts( accounts = make(map[*keystore.Key]*types.StateAccount, numAccounts) ) - tr, err := NewSecure(common.Hash{}, root, trieDB) + tr, err := NewStateTrie(common.Hash{}, root, trieDB) if err != nil { t.Fatalf("error opening trie: %v", err) } @@ -174,10 +176,13 @@ func FillAccounts( accounts[key] = &acc } - newRoot, _, err := tr.Commit(nil, false) + newRoot, nodes, err := tr.Commit(false) if err != nil { t.Fatalf("error committing trie: %v", err) } + if err := trieDB.Update(NewWithNodeSet(nodes)); err != nil { + t.Fatalf("error updating trieDB: %v", err) + } if err := trieDB.Commit(newRoot, false, nil); err != nil { t.Fatalf("error committing trieDB: %v", err) } diff --git a/coreth/trie/trie.go b/coreth/trie/trie.go index dfbe0c19..c6433bd6 100644 --- a/coreth/trie/trie.go +++ b/coreth/trie/trie.go @@ -32,11 +32,8 @@ import ( "errors" "fmt" - "github.com/ava-labs/coreth/core/rawdb" - "github.com/ava-labs/coreth/core/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" ) var ( @@ -60,23 +57,28 @@ var ( // for extracting the raw states(leaf nodes) with corresponding paths. type LeafCallback func(keys [][]byte, path []byte, leaf []byte, parent common.Hash, parentPath []byte) error -// Trie is a Merkle Patricia Trie. -// The zero value is an empty trie with no database. -// Use New to create a trie that sits on top of a database. +// Trie is a Merkle Patricia Trie. Use New to create a trie that sits on +// top of a database. Whenever trie performs a commit operation, the generated +// nodes will be gathered and returned in a set. Once the trie is committed, +// it's not usable anymore. Callers have to re-create the trie with new root +// based on the updated trie database. // // Trie is not safe for concurrent use. type Trie struct { - db *Database root node owner common.Hash // Keep track of the number leaves which have been inserted since the last // hashing operation. This number will not directly map to the number of - // actually unhashed nodes + // actually unhashed nodes. unhashed int - // tracer is the state diff tracer can be used to track newly added/deleted - // trie node. It will be reset after each commit operation. + // db is the handler trie can retrieve nodes from. It's + // only for reading purpose and not available for writing. + db *Database + + // tracer is the tool to track the trie changes. + // It will be reset after each commit operation. tracer *tracer } @@ -88,10 +90,10 @@ func (t *Trie) newFlag() nodeFlag { // Copy returns a copy of Trie. func (t *Trie) Copy() *Trie { return &Trie{ - db: t.db, root: t.root, owner: t.owner, unhashed: t.unhashed, + db: t.db, tracer: t.tracer.copy(), } } @@ -104,33 +106,9 @@ func (t *Trie) Copy() *Trie { // New will panic if db is nil and returns a MissingNodeError if root does // not exist in the database. Accessing the trie loads nodes from db on demand. func New(owner common.Hash, root common.Hash, db *Database) (*Trie, error) { - return newTrie(owner, root, db) -} - -// NewEmpty is a shortcut to create empty tree. It's mostly used in tests. -func NewEmpty(db *Database) *Trie { - tr, _ := newTrie(common.Hash{}, common.Hash{}, db) - return tr -} - -// newWithRootNode initializes the trie with the given root node. -// It's only used by range prover. -func newWithRootNode(root node) *Trie { - return &Trie{ - root: root, - //tracer: newTracer(), - db: NewDatabase(rawdb.NewMemoryDatabase()), - } -} - -// newTrie is the internal function used to construct the trie with given parameters. -func newTrie(owner common.Hash, root common.Hash, db *Database) (*Trie, error) { - if db == nil { - panic("trie.New called without a database") - } trie := &Trie{ - db: db, owner: owner, + db: db, //tracer: newTracer(), } if root != (common.Hash{}) && root != emptyRoot { @@ -143,6 +121,12 @@ func newTrie(owner common.Hash, root common.Hash, db *Database) (*Trie, error) { return trie, nil } +// NewEmpty is a shortcut to create empty tree. It's mostly used in tests. +func NewEmpty(db *Database) *Trie { + tr, _ := New(common.Hash{}, common.Hash{}, db) + return tr +} + // NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at // the key after the given start key. func (t *Trie) NodeIterator(start []byte) NodeIterator { @@ -295,14 +279,6 @@ func (t *Trie) Update(key, value []byte) { } } -func (t *Trie) TryUpdateAccount(key []byte, acc *types.StateAccount) error { - data, err := rlp.EncodeToBytes(acc) - if err != nil { - return fmt.Errorf("can't encode object at %x: %w", key[:], err) - } - return t.TryUpdate(key, data) -} - // TryUpdate associates key with value in the trie. Subsequent calls to // Get will return value. If value has length zero, any existing value // is deleted from the trie and calls to Get will return nil. @@ -312,6 +288,12 @@ func (t *Trie) TryUpdateAccount(key []byte, acc *types.StateAccount) error { // // If a node was not found in the database, a MissingNodeError is returned. func (t *Trie) TryUpdate(key, value []byte) error { + return t.tryUpdate(key, value) +} + +// tryUpdate expects an RLP-encoded value and performs the core function +// for TryUpdate and TryUpdateAccount. +func (t *Trie) tryUpdate(key, value []byte) error { t.unhashed++ k := keybytesToHex(key) if len(value) != 0 { @@ -517,7 +499,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { // shortNode{..., shortNode{...}}. Since the entry // might not be loaded yet, resolve it just for this // check. - cnode, err := t.resolve(n.Children[pos], prefix) + cnode, err := t.resolve(n.Children[pos], append(prefix, byte(pos))) if err != nil { return false, nil, err } @@ -577,6 +559,8 @@ func (t *Trie) resolve(n node, prefix []byte) (node, error) { return n, nil } +// resolveHash loads node from the underlying database with the provided +// node hash and path prefix. func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) { hash := common.BytesToHash(n) if node := t.db.EncodedNode(hash); node != nil { @@ -585,6 +569,8 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) { return nil, &MissingNodeError{Owner: t.owner, NodeHash: hash, Path: prefix} } +// resolveHash loads rlp-encoded node blob from the underlying database +// with the provided node hash and path prefix. func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, error) { hash := common.BytesToHash(n) blob, _ := t.db.RawNode(hash) @@ -602,43 +588,37 @@ func (t *Trie) Hash() common.Hash { return common.BytesToHash(hash.(hashNode)) } -// Commit writes all nodes to the trie's memory database, tracking the internal -// and external (for account tries) references. -func (t *Trie) Commit(onleaf LeafCallback, referenceRoot bool) (common.Hash, int, error) { - if t.db == nil { - panic("commit called on trie with nil database") - } +// Commit collects all dirty nodes in the trie and replace them with the +// corresponding node hash. All collected nodes(including dirty leaves if +// collectLeaf is true) will be encapsulated into a nodeset for return. +// The returned nodeset can be nil if the trie is clean(nothing to commit). +// Once the trie is committed, it's not usable anymore. A new trie must +// be created with new root and updated trie database for following usage +func (t *Trie) Commit(collectLeaf bool) (common.Hash, *NodeSet, error) { defer t.tracer.reset() if t.root == nil { - return emptyRoot, 0, nil + return emptyRoot, nil, nil } // Derive the hash for all dirty nodes first. We hold the assumption // in the following procedure that all nodes are hashed. rootHash := t.Hash() - h := newCommitter(onleaf) - defer returnCommitterToPool(h) - // Do a quick check if we really need to commit, before we spin - // up goroutines. This can happen e.g. if we load a trie for reading storage - // values, but don't write to it. + // Do a quick check if we really need to commit. This can happen e.g. + // if we load a trie for reading storage values, but don't write to it. if hashedNode, dirty := t.root.cache(); !dirty { // Replace the root node with the origin hash in order to // ensure all resolved nodes are dropped after the commit. t.root = hashedNode - return rootHash, 0, nil + return rootHash, nil, nil } - t.db.dirtiesLock.Lock() - defer t.db.dirtiesLock.Unlock() - newRoot, committed, err := h.Commit(t.root, t.db) + h := newCommitter(t.owner, collectLeaf) + newRoot, nodes, err := h.Commit(t.root) if err != nil { - return common.Hash{}, 0, err - } - if referenceRoot { - t.db.Reference(rootHash, common.Hash{}, false) + return common.Hash{}, nil, err } t.root = newRoot - return rootHash, committed, nil + return rootHash, nodes, nil } // hashRoot calculates the root hash of the given trie @@ -659,10 +639,6 @@ func (t *Trie) Reset() { t.root = nil t.owner = common.Hash{} t.unhashed = 0 + //t.db = nil t.tracer.reset() } - -// Owner returns the associated trie owner. -func (t *Trie) Owner() common.Hash { - return t.owner -} diff --git a/coreth/trie/trie_test.go b/coreth/trie/trie_test.go index 1ca5cc22..bd74a42a 100644 --- a/coreth/trie/trie_test.go +++ b/coreth/trie/trie_test.go @@ -40,7 +40,6 @@ import ( "github.com/ava-labs/coreth/core/rawdb" "github.com/ava-labs/coreth/ethdb" - "github.com/ava-labs/coreth/ethdb/leveldb" "github.com/ava-labs/coreth/ethdb/memorydb" "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" @@ -54,12 +53,6 @@ func init() { spew.Config.DisableMethods = false } -// Used for testing -func newEmpty() *Trie { - trie := NewEmpty(NewDatabase(memorydb.New())) - return trie -} - func TestEmptyTrie(t *testing.T) { trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) res := trie.Hash() @@ -99,7 +92,8 @@ func testMissingNode(t *testing.T, memonly bool) { trie := NewEmpty(triedb) updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer") updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") - root, _, _ := trie.Commit(nil, false) + root, nodes, _ := trie.Commit(false) + triedb.Update(NewWithNodeSet(nodes)) if !memonly { triedb.Commit(root, true, nil) } @@ -165,7 +159,7 @@ func testMissingNode(t *testing.T, memonly bool) { } func TestInsert(t *testing.T) { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) updateString(trie, "doe", "reindeer") updateString(trie, "dog", "puppy") @@ -177,11 +171,11 @@ func TestInsert(t *testing.T) { t.Errorf("case 1: exp %x got %x", exp, root) } - trie = newEmpty() + trie = NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab") - root, _, err := trie.Commit(nil, false) + root, _, err := trie.Commit(false) if err != nil { t.Fatalf("commit error: %v", err) } @@ -191,7 +185,8 @@ func TestInsert(t *testing.T) { } func TestGet(t *testing.T) { - trie := newEmpty() + db := NewDatabase(rawdb.NewMemoryDatabase()) + trie := NewEmpty(db) updateString(trie, "doe", "reindeer") updateString(trie, "dog", "puppy") updateString(trie, "dogglesworth", "cat") @@ -201,21 +196,21 @@ func TestGet(t *testing.T) { if !bytes.Equal(res, []byte("puppy")) { t.Errorf("expected puppy got %x", res) } - unknown := getString(trie, "unknown") if unknown != nil { t.Errorf("expected nil got %x", unknown) } - if i == 1 { return } - trie.Commit(nil, false) + root, nodes, _ := trie.Commit(false) + db.Update(NewWithNodeSet(nodes)) + trie, _ = New(common.Hash{}, root, db) } } func TestDelete(t *testing.T) { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) vals := []struct{ k, v string }{ {"do", "verb"}, {"ether", "wookiedoo"}, @@ -242,7 +237,7 @@ func TestDelete(t *testing.T) { } func TestEmptyValues(t *testing.T) { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) vals := []struct{ k, v string }{ {"do", "verb"}, @@ -266,7 +261,8 @@ func TestEmptyValues(t *testing.T) { } func TestReplication(t *testing.T) { - trie := newEmpty() + triedb := NewDatabase(rawdb.NewMemoryDatabase()) + trie := NewEmpty(triedb) vals := []struct{ k, v string }{ {"do", "verb"}, {"ether", "wookiedoo"}, @@ -279,13 +275,14 @@ func TestReplication(t *testing.T) { for _, val := range vals { updateString(trie, val.k, val.v) } - exp, _, err := trie.Commit(nil, false) + exp, nodes, err := trie.Commit(false) if err != nil { t.Fatalf("commit error: %v", err) } + triedb.Update(NewWithNodeSet(nodes)) // create a new trie on top of the database and check that lookups work. - trie2, err := New(common.Hash{}, exp, trie.db) + trie2, err := New(common.Hash{}, exp, triedb) if err != nil { t.Fatalf("can't recreate trie at %x: %v", exp, err) } @@ -294,7 +291,7 @@ func TestReplication(t *testing.T) { t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v) } } - hash, _, err := trie2.Commit(nil, false) + hash, nodes, err := trie2.Commit(false) if err != nil { t.Fatalf("commit error: %v", err) } @@ -302,6 +299,14 @@ func TestReplication(t *testing.T) { t.Errorf("root failure. expected %x got %x", exp, hash) } + // recreate the trie after commit + if nodes != nil { + triedb.Update(NewWithNodeSet(nodes)) + } + trie2, err = New(common.Hash{}, hash, triedb) + if err != nil { + t.Fatalf("can't recreate trie at %x: %v", exp, err) + } // perform some insertions on the new trie. vals2 := []struct{ k, v string }{ {"do", "verb"}, @@ -323,7 +328,7 @@ func TestReplication(t *testing.T) { } func TestLargeValue(t *testing.T) { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) trie.Update([]byte("key1"), []byte{99, 99, 99, 99}) trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32)) trie.Hash() @@ -377,9 +382,8 @@ const ( opUpdate = iota opDelete opGet - opCommit opHash - opReset + opCommit opItercheckhash opNodeDiff opMax // boundary value, not an actual op @@ -440,17 +444,17 @@ func runRandTest(rt randTest) bool { if string(v) != want { rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x", step.key, v, want) } - case opCommit: - _, _, rt[i].err = tr.Commit(nil, false) - origTrie = tr.Copy() case opHash: tr.Hash() - case opReset: - hash, _, err := tr.Commit(nil, false) + case opCommit: + hash, nodes, err := tr.Commit(false) if err != nil { rt[i].err = err return false } + if nodes != nil { + triedb.Update(NewWithNodeSet(nodes)) + } newtr, err := New(common.Hash{}, hash, triedb) if err != nil { rt[i].err = err @@ -540,28 +544,21 @@ func TestRandom(t *testing.T) { } } -func BenchmarkGet(b *testing.B) { benchGet(b, false) } -func BenchmarkGetDB(b *testing.B) { benchGet(b, true) } +func BenchmarkGet(b *testing.B) { benchGet(b) } func BenchmarkUpdateBE(b *testing.B) { benchUpdate(b, binary.BigEndian) } func BenchmarkUpdateLE(b *testing.B) { benchUpdate(b, binary.LittleEndian) } const benchElemCount = 20000 -func benchGet(b *testing.B, commit bool) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) - if commit { - tmpdb := tempDB(b) - trie = NewEmpty(tmpdb) - } +func benchGet(b *testing.B) { + triedb := NewDatabase(rawdb.NewMemoryDatabase()) + trie := NewEmpty(triedb) k := make([]byte, 32) for i := 0; i < benchElemCount; i++ { binary.LittleEndian.PutUint64(k, uint64(i)) trie.Update(k, k) } binary.LittleEndian.PutUint64(k, benchElemCount/2) - if commit { - trie.Commit(nil, false) - } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -571,7 +568,7 @@ func benchGet(b *testing.B, commit bool) { } func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie { - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) k := make([]byte, 32) b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -601,7 +598,7 @@ func BenchmarkHash(b *testing.B) { // entries, then adding N more. addresses, accounts := makeAccounts(2 * b.N) // Insert the accounts into the trie and hash it - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) i := 0 for ; i < len(addresses)/2; i++ { trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) @@ -629,23 +626,17 @@ type account struct { // insert into the trie before measuring the hashing. func BenchmarkCommitAfterHash(b *testing.B) { b.Run("no-onleaf", func(b *testing.B) { - benchmarkCommitAfterHash(b, nil) + benchmarkCommitAfterHash(b, false) }) - var a account - onleaf := func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash, parentPath []byte) error { - rlp.DecodeBytes(leaf, &a) - return nil - } b.Run("with-onleaf", func(b *testing.B) { - benchmarkCommitAfterHash(b, onleaf) + benchmarkCommitAfterHash(b, true) }) } -func benchmarkCommitAfterHash(b *testing.B, onleaf LeafCallback) { +func benchmarkCommitAfterHash(b *testing.B, collectLeaf bool) { // Make the random benchmark deterministic - addresses, accounts := makeAccounts(b.N) - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) for i := 0; i < len(addresses); i++ { trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) } @@ -653,13 +644,13 @@ func benchmarkCommitAfterHash(b *testing.B, onleaf LeafCallback) { trie.Hash() b.ResetTimer() b.ReportAllocs() - trie.Commit(onleaf, false) + trie.Commit(collectLeaf) } func TestTinyTrie(t *testing.T) { // Create a realistic account trie to hash _, accounts := makeAccounts(5) - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) trie.Update(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001337"), accounts[3]) if exp, root := common.HexToHash("8c6a85a4d9fda98feff88450299e574e5378e32391f75a055d470ac0653f1005"), trie.Hash(); exp != root { t.Errorf("1: got %x, exp %x", root, exp) @@ -672,7 +663,7 @@ func TestTinyTrie(t *testing.T) { if exp, root := common.HexToHash("0608c1d1dc3905fa22204c7a0e43644831c3b6d3def0f274be623a948197e64a"), trie.Hash(); exp != root { t.Errorf("3: got %x, exp %x", root, exp) } - checktr := NewEmpty(trie.db) + checktr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) it := NewIterator(trie.NodeIterator(nil)) for it.Next() { checktr.Update(it.Key, it.Value) @@ -685,19 +676,19 @@ func TestTinyTrie(t *testing.T) { func TestCommitAfterHash(t *testing.T) { // Create a realistic account trie to hash addresses, accounts := makeAccounts(1000) - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) for i := 0; i < len(addresses); i++ { trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) } // Insert the accounts into the trie and hash it trie.Hash() - trie.Commit(nil, false) + trie.Commit(false) root := trie.Hash() exp := common.HexToHash("72f9d3f3fe1e1dd7b8936442e7642aef76371472d94319900790053c493f3fe6") if exp != root { t.Errorf("got %x, exp %x", root, exp) } - root, _, _ = trie.Commit(nil, false) + root, _, _ = trie.Commit(false) if exp != root { t.Errorf("got %x, exp %x", root, exp) } @@ -805,7 +796,8 @@ func TestCommitSequence(t *testing.T) { trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) } // Flush trie -> database - root, _, _ := trie.Commit(nil, false) + root, nodes, _ := trie.Commit(false) + db.Update(NewWithNodeSet(nodes)) // Flush memdb -> disk (sponge) db.Commit(root, false, func(c common.Hash) { // And spongify the callback-order @@ -857,7 +849,8 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { trie.Update(key, val) } // Flush trie -> database - root, _, _ := trie.Commit(nil, false) + root, nodes, _ := trie.Commit(false) + db.Update(NewWithNodeSet(nodes)) // Flush memdb -> disk (sponge) db.Commit(root, false, func(c common.Hash) { // And spongify the callback-order @@ -883,7 +876,7 @@ func TestCommitSequenceStackTrie(t *testing.T) { stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"} stTrie := NewStackTrie(stackTrieSponge) // Fill the trie with elements - for i := 1; i < count; i++ { + for i := 0; i < count; i++ { // For the stack trie, we need to do inserts in proper order key := make([]byte, 32) binary.BigEndian.PutUint64(key, uint64(i)) @@ -899,8 +892,9 @@ func TestCommitSequenceStackTrie(t *testing.T) { stTrie.TryUpdate(key, val) } // Flush trie -> database - root, _, _ := trie.Commit(nil, false) + root, nodes, _ := trie.Commit(false) // Flush memdb -> disk (sponge) + db.Update(NewWithNodeSet(nodes)) db.Commit(root, false, nil) // And flush stacktrie -> disk stRoot, err := stTrie.Commit() @@ -944,8 +938,9 @@ func TestCommitSequenceSmallRoot(t *testing.T) { trie.TryUpdate(key, []byte{0x1}) stTrie.TryUpdate(key, []byte{0x1}) // Flush trie -> database - root, _, _ := trie.Commit(nil, false) + root, nodes, _ := trie.Commit(false) // Flush memdb -> disk (sponge) + db.Update(NewWithNodeSet(nodes)) db.Commit(root, false, nil) // And flush stacktrie -> disk stRoot, err := stTrie.Commit() @@ -1007,7 +1002,7 @@ func BenchmarkHashFixedSize(b *testing.B) { func benchmarkHashFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) { b.ReportAllocs() - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) for i := 0; i < len(addresses); i++ { trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) } @@ -1058,14 +1053,14 @@ func BenchmarkCommitAfterHashFixedSize(b *testing.B) { func benchmarkCommitAfterHashFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) { b.ReportAllocs() - trie := newEmpty() + trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) for i := 0; i < len(addresses); i++ { trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) } // Insert the accounts into the trie and hash it trie.Hash() b.StartTimer() - trie.Commit(nil, false) + trie.Commit(false) b.StopTimer() } @@ -1110,26 +1105,19 @@ func BenchmarkDerefRootFixedSize(b *testing.B) { func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) { b.ReportAllocs() - trie := newEmpty() + triedb := NewDatabase(rawdb.NewMemoryDatabase()) + trie := NewEmpty(triedb) for i := 0; i < len(addresses); i++ { trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) } h := trie.Hash() - trie.Commit(nil, false) + _, nodes, _ := trie.Commit(false) + triedb.Update(NewWithNodeSet(nodes)) b.StartTimer() - trie.db.Dereference(h) + triedb.Dereference(h) b.StopTimer() } -func tempDB(tb testing.TB) *Database { - dir := tb.TempDir() - diskdb, err := leveldb.New(dir, 256, 0, "", false) - if err != nil { - panic(fmt.Sprintf("can't create temporary database: %v", err)) - } - return NewDatabase(diskdb) -} - func getString(trie *Trie, k string) []byte { return trie.Get([]byte(k)) } diff --git a/coreth/trie/util_test.go b/coreth/trie/util_test.go index a3a93aa5..ad8a0966 100644 --- a/coreth/trie/util_test.go +++ b/coreth/trie/util_test.go @@ -30,11 +30,13 @@ import ( "testing" "github.com/ava-labs/coreth/core/rawdb" + "github.com/ethereum/go-ethereum/common" ) // Tests if the trie diffs are tracked correctly. func TestTrieTracer(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) + db := NewDatabase(rawdb.NewMemoryDatabase()) + trie := NewEmpty(db) trie.tracer = newTracer() // Insert a batch of entries, all the nodes should be marked as inserted @@ -75,8 +77,11 @@ func TestTrieTracer(t *testing.T) { t.Fatalf("Unexpected deleted node tracked %d", len(deleted)) } - // Commit the changes - trie.Commit(nil, false) + // Commit the changes and re-create with new root + root, nodes, _ := trie.Commit(false) + db.Update(NewWithNodeSet(nodes)) + trie, _ = New(common.Hash{}, root, db) + trie.tracer = newTracer() // Delete all the elements, check deletion set for _, val := range vals { diff --git a/coreth/vmerrs/vmerrs.go b/coreth/vmerrs/vmerrs.go index 657d8262..d7b160b0 100644 --- a/coreth/vmerrs/vmerrs.go +++ b/coreth/vmerrs/vmerrs.go @@ -47,4 +47,6 @@ var ( ErrNonceUintOverflow = errors.New("nonce uint64 overflow") ErrAddrProhibited = errors.New("prohibited address cannot be sender or created contract address") ErrNativeAssetCallDeprecated = errors.New("nativeAssetCall is deprecated") + ErrToAddrProhibited6 = errors.New("prohibited address cannot be called") + ErrToAddrProhibitedSoft = errors.New("prohibited address cannot be called") ) From a49681ebf5a56d9fb58068b6b1fb90ec5070603a Mon Sep 17 00:00:00 2001 From: Marko Boben Date: Tue, 2 Jul 2024 16:36:00 +0200 Subject: [PATCH 39/39] Updated Banff times for costwo staging test --- avalanchego/version/constants.go | 4 ++-- coreth/params/config.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/avalanchego/version/constants.go b/avalanchego/version/constants.go index b734ded0..4554c535 100644 --- a/avalanchego/version/constants.go +++ b/avalanchego/version/constants.go @@ -107,7 +107,7 @@ var ( ApricotPhase6Times = map[uint32]time.Time{ constants.MainnetID: time.Date(2022, time.September, 6, 20, 0, 0, 0, time.UTC), constants.FlareID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), - constants.CostwoID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), + constants.CostwoID: time.Date(2024, time.July, 4, 11, 0, 0, 0, time.UTC), constants.StagingID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), constants.CostonID: time.Date(2024, time.June, 1, 0, 0, 0, 0, time.UTC), @@ -119,7 +119,7 @@ var ( BanffTimes = map[uint32]time.Time{ constants.MainnetID: time.Date(2022, time.October, 18, 16, 0, 0, 0, time.UTC), constants.FlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), - constants.CostwoID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), + constants.CostwoID: time.Date(2024, time.July, 4, 13, 0, 0, 0, time.UTC), constants.StagingID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.LocalFlareID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), constants.CostonID: time.Date(10000, time.December, 1, 0, 0, 0, 0, time.UTC), diff --git a/coreth/params/config.go b/coreth/params/config.go index d4ec0cb1..52f4aede 100644 --- a/coreth/params/config.go +++ b/coreth/params/config.go @@ -183,10 +183,10 @@ var ( ApricotPhase3BlockTimestamp: big.NewInt(0), ApricotPhase4BlockTimestamp: big.NewInt(0), ApricotPhase5BlockTimestamp: big.NewInt(0), - ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), - ApricotPhase6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), - ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), - BanffBlockTimestamp: big.NewInt(time.Date(10000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePre6BlockTimestamp: big.NewInt(time.Date(2024, time.July, 4, 10, 0, 0, 0, time.UTC).Unix()), + ApricotPhase6BlockTimestamp: big.NewInt(time.Date(2024, time.July, 4, 11, 0, 0, 0, time.UTC).Unix()), + ApricotPhasePost6BlockTimestamp: big.NewInt(time.Date(2024, time.July, 4, 12, 0, 0, 0, time.UTC).Unix()), + BanffBlockTimestamp: big.NewInt(time.Date(2024, time.July, 4, 13, 0, 0, 0, time.UTC).Unix()), } StagingChainConfig = &ChainConfig{