diff --git a/Makefile b/Makefile index 1a46832ec..de841b69d 100644 --- a/Makefile +++ b/Makefile @@ -420,7 +420,7 @@ test_verbose: check_go_version ## Run all go tests verbosely go test -count=1 -v -race -tags test ./... .PHONY: test_all -test_all: check_go_version ## Run all go tests showing detailed output only on failures +test_all: warn_flaky_tests check_go_version ## Run all go tests showing detailed output only on failures go test -count=1 -race -tags test ./... .PHONY: test_all_with_integration @@ -503,17 +503,22 @@ go_develop_and_test: go_develop test_all ## Generate protos, mocks and run all t # TODO_DISCUSS_IN_THIS_COMMIT - SHOULD NEVER BE COMMITTED TO MASTER. It is a way for the reviewer of a PR to start / reply to a discussion. # TODO_IN_THIS_COMMIT - SHOULD NEVER BE COMMITTED TO MASTER. It is a way to start the review process while non-critical changes are still in progress + +# Define shared variable for the exclude parameters +EXCLUDE_GREP = --exclude-dir={.git,vendor,./docusaurus,.vscode,.idea} --exclude={Makefile,reviewdog.yml,*.pb.go,*.pulsar.go} + .PHONY: todo_list todo_list: ## List all the TODOs in the project (excludes vendor and prototype directories) - grep --exclude-dir={.git,vendor,./docusaurus} -r TODO . + grep -r $(EXCLUDE_GREP) TODO . | grep -v 'TODO()' .PHONY: todo_count todo_count: ## Print a count of all the TODOs in the project - grep --exclude-dir={.git,vendor,./docusaurus} -r TODO . | wc -l + grep -r $(EXCLUDE_GREP) TODO . | grep -v 'TODO()' | wc -l .PHONY: todo_this_commit todo_this_commit: ## List all the TODOs needed to be done in this commit - grep -n --exclude-dir={.git,vendor,.vscode,.idea} --exclude={Makefile,reviewdog.yml} -r -e "TODO_IN_THIS_" + grep -r $(EXCLUDE_GREP) TODO_IN_THIS .| grep -v 'TODO()' + #################### ### Gateways ### @@ -809,6 +814,19 @@ warn_message_local_stress_test: ## Print a warning message when kicking off a lo @echo "| |" @echo "+-----------------------------------------------------------------------------------------------+" +PHONY: warn_flaky_tests +warn_flaky_tests: ## Print a warning message that some unit tests may be flaky + @echo "+-----------------------------------------------------------------------------------------------+" + @echo "| |" + @echo "| IMPORTANT: READ ME IF YOUR TESTS FAIL!!! |" + @echo "| |" + @echo "| 1. Our unit / integration tests are far from perfect & some are flaky |" + @echo "| 2. If you ran 'make go_develop_and_test' and a failure occurred, try to run: |" + @echo "| 'make test_all' once or twice more |" + @echo "| 3. If the same error persists, isolate it with 'go test -v ./path/to/failing/module |" + @echo "| |" + @echo "+-----------------------------------------------------------------------------------------------+" + ############## ### Claims ### ############## diff --git a/api/poktroll/proof/params.pulsar.go b/api/poktroll/proof/params.pulsar.go index 5c642a5fb..b8162ea2b 100644 --- a/api/poktroll/proof/params.pulsar.go +++ b/api/poktroll/proof/params.pulsar.go @@ -18,17 +18,17 @@ import ( ) var ( - md_Params protoreflect.MessageDescriptor - fd_Params_min_relay_difficulty_bits protoreflect.FieldDescriptor - fd_Params_proof_request_probability protoreflect.FieldDescriptor - fd_Params_proof_requirement_threshold protoreflect.FieldDescriptor - fd_Params_proof_missing_penalty protoreflect.FieldDescriptor + md_Params protoreflect.MessageDescriptor + fd_Params_relay_difficulty_target_hash protoreflect.FieldDescriptor + fd_Params_proof_request_probability protoreflect.FieldDescriptor + fd_Params_proof_requirement_threshold protoreflect.FieldDescriptor + fd_Params_proof_missing_penalty protoreflect.FieldDescriptor ) func init() { file_poktroll_proof_params_proto_init() md_Params = File_poktroll_proof_params_proto.Messages().ByName("Params") - fd_Params_min_relay_difficulty_bits = md_Params.Fields().ByName("min_relay_difficulty_bits") + fd_Params_relay_difficulty_target_hash = md_Params.Fields().ByName("relay_difficulty_target_hash") fd_Params_proof_request_probability = md_Params.Fields().ByName("proof_request_probability") fd_Params_proof_requirement_threshold = md_Params.Fields().ByName("proof_requirement_threshold") fd_Params_proof_missing_penalty = md_Params.Fields().ByName("proof_missing_penalty") @@ -99,9 +99,9 @@ func (x *fastReflection_Params) Interface() protoreflect.ProtoMessage { // While iterating, mutating operations may only be performed // on the current field descriptor. func (x *fastReflection_Params) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { - if x.MinRelayDifficultyBits != uint64(0) { - value := protoreflect.ValueOfUint64(x.MinRelayDifficultyBits) - if !f(fd_Params_min_relay_difficulty_bits, value) { + if len(x.RelayDifficultyTargetHash) != 0 { + value := protoreflect.ValueOfBytes(x.RelayDifficultyTargetHash) + if !f(fd_Params_relay_difficulty_target_hash, value) { return } } @@ -138,8 +138,8 @@ func (x *fastReflection_Params) Range(f func(protoreflect.FieldDescriptor, proto // a repeated field is populated if it is non-empty. func (x *fastReflection_Params) Has(fd protoreflect.FieldDescriptor) bool { switch fd.FullName() { - case "poktroll.proof.Params.min_relay_difficulty_bits": - return x.MinRelayDifficultyBits != uint64(0) + case "poktroll.proof.Params.relay_difficulty_target_hash": + return len(x.RelayDifficultyTargetHash) != 0 case "poktroll.proof.Params.proof_request_probability": return x.ProofRequestProbability != float32(0) || math.Signbit(float64(x.ProofRequestProbability)) case "poktroll.proof.Params.proof_requirement_threshold": @@ -162,8 +162,8 @@ func (x *fastReflection_Params) Has(fd protoreflect.FieldDescriptor) bool { // Clear is a mutating operation and unsafe for concurrent use. func (x *fastReflection_Params) Clear(fd protoreflect.FieldDescriptor) { switch fd.FullName() { - case "poktroll.proof.Params.min_relay_difficulty_bits": - x.MinRelayDifficultyBits = uint64(0) + case "poktroll.proof.Params.relay_difficulty_target_hash": + x.RelayDifficultyTargetHash = nil case "poktroll.proof.Params.proof_request_probability": x.ProofRequestProbability = float32(0) case "poktroll.proof.Params.proof_requirement_threshold": @@ -186,9 +186,9 @@ func (x *fastReflection_Params) Clear(fd protoreflect.FieldDescriptor) { // of the value; to obtain a mutable reference, use Mutable. func (x *fastReflection_Params) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { switch descriptor.FullName() { - case "poktroll.proof.Params.min_relay_difficulty_bits": - value := x.MinRelayDifficultyBits - return protoreflect.ValueOfUint64(value) + case "poktroll.proof.Params.relay_difficulty_target_hash": + value := x.RelayDifficultyTargetHash + return protoreflect.ValueOfBytes(value) case "poktroll.proof.Params.proof_request_probability": value := x.ProofRequestProbability return protoreflect.ValueOfFloat32(value) @@ -218,8 +218,8 @@ func (x *fastReflection_Params) Get(descriptor protoreflect.FieldDescriptor) pro // Set is a mutating operation and unsafe for concurrent use. func (x *fastReflection_Params) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { switch fd.FullName() { - case "poktroll.proof.Params.min_relay_difficulty_bits": - x.MinRelayDifficultyBits = value.Uint() + case "poktroll.proof.Params.relay_difficulty_target_hash": + x.RelayDifficultyTargetHash = value.Bytes() case "poktroll.proof.Params.proof_request_probability": x.ProofRequestProbability = float32(value.Float()) case "poktroll.proof.Params.proof_requirement_threshold": @@ -251,8 +251,8 @@ func (x *fastReflection_Params) Mutable(fd protoreflect.FieldDescriptor) protore x.ProofMissingPenalty = new(v1beta1.Coin) } return protoreflect.ValueOfMessage(x.ProofMissingPenalty.ProtoReflect()) - case "poktroll.proof.Params.min_relay_difficulty_bits": - panic(fmt.Errorf("field min_relay_difficulty_bits of message poktroll.proof.Params is not mutable")) + case "poktroll.proof.Params.relay_difficulty_target_hash": + panic(fmt.Errorf("field relay_difficulty_target_hash of message poktroll.proof.Params is not mutable")) case "poktroll.proof.Params.proof_request_probability": panic(fmt.Errorf("field proof_request_probability of message poktroll.proof.Params is not mutable")) case "poktroll.proof.Params.proof_requirement_threshold": @@ -270,8 +270,8 @@ func (x *fastReflection_Params) Mutable(fd protoreflect.FieldDescriptor) protore // For lists, maps, and messages, this returns a new, empty, mutable value. func (x *fastReflection_Params) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "poktroll.proof.Params.min_relay_difficulty_bits": - return protoreflect.ValueOfUint64(uint64(0)) + case "poktroll.proof.Params.relay_difficulty_target_hash": + return protoreflect.ValueOfBytes(nil) case "poktroll.proof.Params.proof_request_probability": return protoreflect.ValueOfFloat32(float32(0)) case "poktroll.proof.Params.proof_requirement_threshold": @@ -348,8 +348,9 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { var n int var l int _ = l - if x.MinRelayDifficultyBits != 0 { - n += 1 + runtime.Sov(uint64(x.MinRelayDifficultyBits)) + l = len(x.RelayDifficultyTargetHash) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) } if x.ProofRequestProbability != 0 || math.Signbit(float64(x.ProofRequestProbability)) { n += 5 @@ -415,10 +416,12 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { i-- dAtA[i] = 0x15 } - if x.MinRelayDifficultyBits != 0 { - i = runtime.EncodeVarint(dAtA, i, uint64(x.MinRelayDifficultyBits)) + if len(x.RelayDifficultyTargetHash) > 0 { + i -= len(x.RelayDifficultyTargetHash) + copy(dAtA[i:], x.RelayDifficultyTargetHash) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.RelayDifficultyTargetHash))) i-- - dAtA[i] = 0x8 + dAtA[i] = 0xa } if input.Buf != nil { input.Buf = append(input.Buf, dAtA...) @@ -470,10 +473,10 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { } switch fieldNum { case 1: - if wireType != 0 { - return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field MinRelayDifficultyBits", wireType) + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field RelayDifficultyTargetHash", wireType) } - x.MinRelayDifficultyBits = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow @@ -483,11 +486,26 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { } b := dAtA[iNdEx] iNdEx++ - x.MinRelayDifficultyBits |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } + if byteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.RelayDifficultyTargetHash = append(x.RelayDifficultyTargetHash[:0], dAtA[iNdEx:postIndex]...) + if x.RelayDifficultyTargetHash == nil { + x.RelayDifficultyTargetHash = []byte{} + } + iNdEx = postIndex case 2: if wireType != 5 { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ProofRequestProbability", wireType) @@ -608,9 +626,9 @@ type Params struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // min_relay_difficulty_bits is the minimum difficulty in bits for a relay to - // be included in a Merkle proof. - MinRelayDifficultyBits uint64 `protobuf:"varint,1,opt,name=min_relay_difficulty_bits,json=minRelayDifficultyBits,proto3" json:"min_relay_difficulty_bits,omitempty"` + // TODO_FOLLOWUP(@olshansk, #690): Either delete this or change it to be named "minimum" + // relay_difficulty_target_hash is the maximum value a relay hash must be less than to be volume/reward applicable. + RelayDifficultyTargetHash []byte `protobuf:"bytes,1,opt,name=relay_difficulty_target_hash,json=relayDifficultyTargetHash,proto3" json:"relay_difficulty_target_hash,omitempty"` // proof_request_probability is the probability of a session requiring a proof // if it's cost (i.e. compute unit consumption) is below the ProofRequirementThreshold. ProofRequestProbability float32 `protobuf:"fixed32,2,opt,name=proof_request_probability,json=proofRequestProbability,proto3" json:"proof_request_probability,omitempty"` @@ -647,11 +665,11 @@ func (*Params) Descriptor() ([]byte, []int) { return file_poktroll_proof_params_proto_rawDescGZIP(), []int{0} } -func (x *Params) GetMinRelayDifficultyBits() uint64 { +func (x *Params) GetRelayDifficultyTargetHash() []byte { if x != nil { - return x.MinRelayDifficultyBits + return x.RelayDifficultyTargetHash } - return 0 + return nil } func (x *Params) GetProofRequestProbability() float32 { @@ -685,44 +703,45 @@ var file_poktroll_proof_params_proto_rawDesc = []byte{ 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x69, 0x6e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x03, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x12, 0x58, 0x0a, 0x19, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x64, - 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x1d, 0xea, 0xde, 0x1f, 0x19, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x79, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x5f, 0x62, - 0x69, 0x74, 0x73, 0x52, 0x16, 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x44, 0x69, 0x66, - 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x42, 0x69, 0x74, 0x73, 0x12, 0x59, 0x0a, 0x19, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, - 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x42, 0x1d, - 0xea, 0xde, 0x1f, 0x19, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x17, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x5f, 0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x03, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x12, 0x61, 0x0a, 0x1c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, + 0x63, 0x75, 0x6c, 0x74, 0x79, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x20, 0xea, 0xde, 0x1f, 0x1c, 0x72, 0x65, 0x6c, + 0x61, 0x79, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x5f, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x52, 0x19, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x59, 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x42, 0x1d, 0xea, 0xde, 0x1f, 0x19, 0x70, 0x72, 0x6f, + 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x17, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, + 0x5f, 0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x1f, 0xea, 0xde, 0x1f, 0x1b, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x1f, 0xea, 0xde, 0x1f, - 0x1b, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x52, 0x19, 0x70, 0x72, - 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x68, - 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x68, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x6f, 0x66, - 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x6e, 0x61, 0x6c, 0x74, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, - 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x69, - 0x6e, 0x42, 0x19, 0xea, 0xde, 0x1f, 0x15, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x6e, 0x61, 0x6c, 0x74, 0x79, 0x52, 0x13, 0x70, 0x72, - 0x6f, 0x6f, 0x66, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x6e, 0x61, 0x6c, 0x74, - 0x79, 0x3a, 0x20, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x17, 0x70, 0x6f, 0x6b, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x78, 0x2f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x2f, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x42, 0x9b, 0x01, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6b, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x0b, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x1f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, - 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6b, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0xa2, 0x02, 0x03, 0x50, 0x50, 0x58, - 0xaa, 0x02, 0x0e, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0xca, 0x02, 0x0e, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x5c, 0x50, 0x72, 0x6f, - 0x6f, 0x66, 0xe2, 0x02, 0x1a, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x5c, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x0f, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x52, 0x19, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, + 0x12, 0x68, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x5f, 0x70, 0x65, 0x6e, 0x61, 0x6c, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x42, 0x19, 0xea, 0xde, 0x1f, 0x15, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, + 0x6e, 0x61, 0x6c, 0x74, 0x79, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x50, 0x65, 0x6e, 0x61, 0x6c, 0x74, 0x79, 0x3a, 0x20, 0xe8, 0xa0, 0x1f, 0x01, + 0x8a, 0xe7, 0xb0, 0x2a, 0x17, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x78, 0x2f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x2f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x9b, 0x01, 0x0a, + 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x70, 0x72, + 0x6f, 0x6f, 0x66, 0x42, 0x0b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x1f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x70, 0x72, + 0x6f, 0x6f, 0x66, 0xa2, 0x02, 0x03, 0x50, 0x50, 0x58, 0xaa, 0x02, 0x0e, 0x50, 0x6f, 0x6b, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0xca, 0x02, 0x0e, 0x50, 0x6f, 0x6b, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x5c, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0xe2, 0x02, 0x1a, 0x50, 0x6f, + 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x5c, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0f, 0x50, 0x6f, 0x6b, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/e2e/tests/parse_params_test.go b/e2e/tests/parse_params_test.go index 7e9a3a8c6..2f3d5e99c 100644 --- a/e2e/tests/parse_params_test.go +++ b/e2e/tests/parse_params_test.go @@ -3,6 +3,7 @@ package e2e import ( + "encoding/hex" "fmt" "strconv" @@ -132,8 +133,8 @@ func (s *suite) newProofMsgUpdateParams(params paramsMap) cosmostypes.Msg { for paramName, paramValue := range params { switch paramName { - case prooftypes.ParamMinRelayDifficultyBits: - msgUpdateParams.Params.MinRelayDifficultyBits = uint64(paramValue.value.(int64)) + case prooftypes.ParamRelayDifficultyTargetHash: + msgUpdateParams.Params.RelayDifficultyTargetHash, _ = hex.DecodeString(string(paramValue.value.([]byte))) case prooftypes.ParamProofRequestProbability: msgUpdateParams.Params.ProofRequestProbability = paramValue.value.(float32) case prooftypes.ParamProofRequirementThreshold: diff --git a/e2e/tests/update_params.feature b/e2e/tests/update_params.feature index 3430b6549..9804553da 100644 --- a/e2e/tests/update_params.feature +++ b/e2e/tests/update_params.feature @@ -37,11 +37,11 @@ Feature: Params Namespace And all "proof" module params are set to their default values And an authz grant from the "gov" "module" account to the "pnf" "user" account for the "/poktroll.proof.MsgUpdateParams" message exists When the "pnf" account sends an authz exec message to update all "proof" module params - | name | value | type | - | min_relay_difficulty_bits | 8 | int64 | - | proof_request_probability | 0.1 | float | - | proof_requirement_threshold | 100 | int64 | - | proof_missing_penalty | 500 | coin | + | name | value | type | + | relay_difficulty_target_hash | 00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff | bytes | + | proof_request_probability | 0.1 | float | + | proof_requirement_threshold | 100 | int64 | + | proof_missing_penalty | 500 | coin | Then all "proof" module params should be updated # NB: If you are reading this and the proof module has parameters @@ -89,6 +89,6 @@ Feature: Params Namespace And all "proof" module params are set to their default values And an authz grant from the "gov" "module" account to the "pnf" "user" account for the "/poktroll.proof.MsgUpdateParams" message exists When the "unauthorized" account sends an authz exec message to update "proof" the module param - | name | value | type | - | "min_relay_difficulty_bits | 666 | int64 | + | name | value | type | + | proof_request_probability | 0.1 | float | Then the "proof" module param "min_relay_difficulty_bits" should be set to its default value diff --git a/e2e/tests/update_params_test.go b/e2e/tests/update_params_test.go index 6e618997d..c7d383479 100644 --- a/e2e/tests/update_params_test.go +++ b/e2e/tests/update_params_test.go @@ -3,6 +3,7 @@ package e2e import ( + "encoding/hex" "encoding/json" "fmt" "reflect" @@ -370,9 +371,9 @@ func (s *suite) assertExpectedModuleParamsUpdated(moduleName string) { params := prooftypes.DefaultParams() paramsMap := s.expectedModuleParams[moduleName] - minRelayDifficultyBits, ok := paramsMap[prooftypes.ParamMinRelayDifficultyBits] + relayDifficultyTargetHash, ok := paramsMap[prooftypes.ParamRelayDifficultyTargetHash] if ok { - params.MinRelayDifficultyBits = uint64(minRelayDifficultyBits.value.(int64)) + params.RelayDifficultyTargetHash, _ = hex.DecodeString(string(relayDifficultyTargetHash.value.([]byte))) } proofRequestProbability, ok := paramsMap[prooftypes.ParamProofRequestProbability] diff --git a/pkg/client/events/query_client_test.go b/pkg/client/events/query_client_test.go index 2c868ac42..2130d350b 100644 --- a/pkg/client/events/query_client_test.go +++ b/pkg/client/events/query_client_test.go @@ -366,7 +366,6 @@ func behavesLikeEitherObserver[V any]( require.NoError(t, err) require.Equal(t, notificationsLimit, int(atomic.LoadInt32(&eventsCounter))) - // TODO_THIS_COMMIT: is this necessary? time.Sleep(10 * time.Millisecond) if onLimit != nil { diff --git a/pkg/client/interface.go b/pkg/client/interface.go index ff5375596..a5cec3ae8 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -335,7 +335,7 @@ type BlockQueryClient interface { // protobuf message. Since the generated go types don't include interface types, this // is necessary to prevent dependency cycles. type ProofParams interface { - GetMinRelayDifficultyBits() uint64 + GetRelayDifficultyTargetHash() []byte GetProofRequestProbability() float32 GetProofRequirementThreshold() uint64 GetProofMissingPenalty() *cosmostypes.Coin diff --git a/pkg/crypto/protocol/difficulty.go b/pkg/crypto/protocol/difficulty.go index 8901f0758..8d7ccb835 100644 --- a/pkg/crypto/protocol/difficulty.go +++ b/pkg/crypto/protocol/difficulty.go @@ -1,20 +1,45 @@ package protocol import ( - "encoding/binary" - "math/bits" + "bytes" + "encoding/hex" + "math/big" ) -// CountHashDifficultyBits returns the number of leading zero bits in the given byte slice. -// TODO_MAINNET: Consider generalizing difficulty to a target hash. See: -// - https://bitcoin.stackexchange.com/questions/107976/bitcoin-difficulty-why-leading-0s -// - https://bitcoin.stackexchange.com/questions/121920/is-it-always-possible-to-find-a-number-whose-hash-starts-with-a-certain-number-o -// - https://github.com/pokt-network/poktroll/pull/656/files#r1666712528 -func CountHashDifficultyBits(bz [32]byte) int { - // Using BigEndian for contiguous bit/byte ordering such leading zeros - // accumulate across adjacent bytes. - // E.g.: []byte{0, 0b00111111, 0x00, 0x00} has 10 leading zero bits. If - // LittleEndian were applied instead, it would have 18 leading zeros because it would - // look like []byte{0, 0, 0b00111111, 0}. - return bits.LeadingZeros64(binary.BigEndian.Uint64(bz[:])) +var ( + // BaseRelayDifficultyHashBz is the chosen "highest" (easiest) target hash, which + // corresponds to the lowest possible difficulty. + // + // It effectively normalizes the difficulty number (which is returned by GetDifficultyFromHash) + // by defining the hash which corresponds to the base difficulty. + // + // When this is the difficulty of a particular service, all relays are reward / volume applicable. + // + // Bitcoin uses a similar concept, where the target hash is defined as the hash: + // - https://bitcoin.stackexchange.com/questions/107976/bitcoin-difficulty-why-leading-0s + // - https://bitcoin.stackexchange.com/questions/121920/is-it-always-possible-to-find-a-number-whose-hash-starts-with-a-certain-number-o + BaseRelayDifficultyHashBz, _ = hex.DecodeString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") +) + +// GetDifficultyFromHash returns the "difficulty" of the given hash, with respect +// to the "highest" (easiest) target hash, BaseRelayDifficultyHash. +// The resultant value is not used for any business logic but is simplify there to have a human-readable version of the hash. +func GetDifficultyFromHash(hashBz [RelayHasherSize]byte) int64 { + baseRelayDifficultyHashInt := new(big.Int).SetBytes(BaseRelayDifficultyHashBz) + hashInt := new(big.Int).SetBytes(hashBz[:]) + + // difficulty is the ratio of the highest target hash to the given hash. + // TODO_MAINNET: Can this cause an integer overflow? + return new(big.Int).Div(baseRelayDifficultyHashInt, hashInt).Int64() +} + +// IsRelayVolumeApplicable returns true if the relay IS reward / volume applicable. +// A relay is reward / volume applicable IFF its hash is less than the target hash. +// - relayHash is the hash of the relay to be checked. +// - targetHash is the hash of the relay difficulty target for a particular service. +// +// TODO_MAINNET: Devise a test that tries to attack the network and ensure that +// there is sufficient telemetry. +func IsRelayVolumeApplicable(relayHash, targetHash []byte) bool { + return bytes.Compare(relayHash, targetHash) == -1 // True if relayHash < targetHash } diff --git a/pkg/crypto/protocol/difficulty_test.go b/pkg/crypto/protocol/difficulty_test.go index 90a9a2367..199862ccf 100644 --- a/pkg/crypto/protocol/difficulty_test.go +++ b/pkg/crypto/protocol/difficulty_test.go @@ -1,51 +1,106 @@ -package protocol_test +package protocol import ( - "fmt" + "encoding/hex" + "math/big" "testing" "github.com/stretchr/testify/require" - - "github.com/pokt-network/poktroll/pkg/crypto/protocol" ) -func TestCountDifficultyBits(t *testing.T) { +func TestGetDifficultyFromHash(t *testing.T) { tests := []struct { - bz []byte - difficulty int + desc string + hashHex string + expectedDifficulty int64 }{ { - bz: []byte{0b11111111}, - difficulty: 0, + desc: "Difficulty 1", + hashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedDifficulty: 1, + }, + { + desc: "Difficulty 2", + hashHex: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedDifficulty: 2, + }, + { + desc: "Difficulty 4", + hashHex: "3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedDifficulty: 4, }, { - bz: []byte{0b01111111}, - difficulty: 1, + desc: "Highest difficulty", + hashHex: "0000000000000000000000000000000000000000000000000000000000000001", + expectedDifficulty: new(big.Int).SetBytes(BaseRelayDifficultyHashBz).Int64(), + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + hashBytes, err := hex.DecodeString(test.hashHex) + if err != nil { + t.Fatalf("failed to decode hash: %v", err) + } + + var hashBz [RelayHasherSize]byte + copy(hashBz[:], hashBytes) + + difficulty := GetDifficultyFromHash(hashBz) + t.Logf("test: %s, difficulty: %d", test.desc, difficulty) + require.Equal(t, test.expectedDifficulty, difficulty) + }) + } +} + +func TestIsRelayVolumeApplicable(t *testing.T) { + tests := []struct { + desc string + relayHashHex string + targetHashHex string + expectedVolumeApplicable bool + }{ + { + desc: "Applicable: relayHash << targetHash", + relayHashHex: "000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + targetHashHex: "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedVolumeApplicable: true, }, { - bz: []byte{0, 255}, - difficulty: 8, + desc: "Applicable: relayHash < targetHash", + relayHashHex: "00efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + targetHashHex: "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedVolumeApplicable: true, }, { - bz: []byte{0, 0b01111111}, - difficulty: 9, + desc: "Not Applicable: relayHash = targetHash", + relayHashHex: "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + targetHashHex: "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedVolumeApplicable: false, }, { - bz: []byte{0, 0b00111111}, - difficulty: 10, + desc: "Not applicable: relayHash > targetHash", + relayHashHex: "0effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + targetHashHex: "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedVolumeApplicable: false, }, { - bz: []byte{0, 0, 255}, - difficulty: 16, + desc: "Not applicable: relayHash >> targetHash", + relayHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + targetHashHex: "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expectedVolumeApplicable: false, }, } for _, test := range tests { - t.Run(fmt.Sprintf("difficulty_%d_zero_bits", test.difficulty), func(t *testing.T) { - var bz [32]byte - copy(bz[:], test.bz) - actualDifficulty := protocol.CountHashDifficultyBits(bz) - require.Equal(t, test.difficulty, actualDifficulty) + t.Run(test.desc, func(t *testing.T) { + relayHash, err := hex.DecodeString(test.relayHashHex) + require.NoError(t, err) + + targetHash, err := hex.DecodeString(test.targetHashHex) + require.NoError(t, err) + + require.Equal(t, test.expectedVolumeApplicable, IsRelayVolumeApplicable(relayHash, targetHash)) }) } } diff --git a/pkg/crypto/protocol/hash.go b/pkg/crypto/protocol/hash.go new file mode 100644 index 000000000..4ef69514c --- /dev/null +++ b/pkg/crypto/protocol/hash.go @@ -0,0 +1,15 @@ +package protocol + +// GetRelayHashFromBytes returns the hash of the relay (full, request or response) bytes. +// It is used as helper in the case that the relay is already marshaled and +// centralizes the hasher used. +func GetRelayHashFromBytes(relayBz []byte) (hash [RelayHasherSize]byte) { + hasher := NewRelayHasher() + + // NB: Intentionally ignoring the error, following sha256.Sum256 implementation. + _, _ = hasher.Write(relayBz) + hashBz := hasher.Sum(nil) + copy(hash[:], hashBz) + + return hash +} diff --git a/pkg/crypto/protocol/hasher.go b/pkg/crypto/protocol/hasher.go index 447c202ec..e5f008c1a 100644 --- a/pkg/crypto/protocol/hasher.go +++ b/pkg/crypto/protocol/hasher.go @@ -3,10 +3,13 @@ package protocol import "crypto/sha256" const ( - TrieHasherSize = sha256.Size - TrieRootSize = TrieHasherSize + trieRootMetadataSize - // TODO_CONSIDERATION: Export this from the SMT package. - trieRootMetadataSize = 16 + RelayHasherSize = sha256.Size + TrieHasherSize = sha256.Size + TrieRootSize = TrieHasherSize + trieRootMetadataSize + trieRootMetadataSize = 16 // TODO_CONSIDERATION: Export this from the SMT package. ) -var NewTrieHasher = sha256.New +var ( + NewRelayHasher = sha256.New + NewTrieHasher = sha256.New +) diff --git a/pkg/crypto/rand/integer.go b/pkg/crypto/rand/integer.go index 683211ed0..9c72bc483 100644 --- a/pkg/crypto/rand/integer.go +++ b/pkg/crypto/rand/integer.go @@ -17,6 +17,16 @@ import ( func SeededInt63(seedParts ...[]byte) int64 { seedHashInputBz := bytes.Join(append([][]byte{}, seedParts...), nil) seedHash := crypto.Sha256(seedHashInputBz) + + // TODO_MAINNET: To support other language implementations of the protocol, the + // pseudo-random number generator used here should be language-agnostic (i.e. not + // golang specific). + // + // Additionally, there is a precision loss here when converting the hash to an int64. + // Since the math/rand.Source interface only supports int64 seeds, we are forced to + // truncate the hash to 64 bits. This is not ideal, as it reduces the entropy of the + // seed. We should consider using a different random number generator that supports + // byte array seeds. seed, _ := binary.Varint(seedHash) return rand.NewSource(seed).Int63() diff --git a/pkg/relayer/miner/gen/gen_fixtures.go b/pkg/relayer/miner/gen/gen_fixtures.go index 0531e4266..9c6e7fbe5 100644 --- a/pkg/relayer/miner/gen/gen_fixtures.go +++ b/pkg/relayer/miner/gen/gen_fixtures.go @@ -32,6 +32,7 @@ const ( defaultOutPath = "relay_fixtures_test.go" ) +// TODO_FOLLOWUP(@olshansk, #690): Do a global anycase grep for "DifficultyBits" and update/remove things appropriately. var ( // flagDifficultyBitsThreshold is the number of leading zero bits that a // randomized, serialized relay must have to be included in the @@ -152,7 +153,6 @@ func genRandomizedMinedRelayFixtures( Res: nil, } - // TODO_TECHDEBT(@red-0ne): use canonical codec. relayBz, err := relay.Marshal() if err != nil { errCh <- err diff --git a/pkg/relayer/miner/miner.go b/pkg/relayer/miner/miner.go index c474d9c15..c5f7a5605 100644 --- a/pkg/relayer/miner/miner.go +++ b/pkg/relayer/miner/miner.go @@ -25,9 +25,12 @@ type miner struct { // proofQueryClient is used to query for the minimum relay difficulty. proofQueryClient client.ProofQueryClient - // relayDifficultyBits is the minimum difficulty that a relay must have to be - // volume / reward applicable. - relayDifficultyBits uint64 + // relay_difficulty is the target hash which a relay hash must be less than to be volume/reward applicable. + // + // TODO_MAINNET(#543): This is populated by querying the corresponding on-chain parameter during construction. + // If this parameter is updated on-chain the relayminer will need to be restarted to query the new value. + // TODO_FOLLOWUP(@olshansk, #690): This needs to be maintained (and updated) on a per service level. + relayDifficultyTargetHash []byte } // NewMiner creates a new miner from the given dependencies and options. It @@ -37,7 +40,7 @@ type miner struct { // - ProofQueryClient // // Available options: -// - WithDifficulty +// - WithRelayDifficultyTargetHash func NewMiner( deps depinject.Config, opts ...relayer.MinerOption, @@ -91,8 +94,8 @@ func (mnr *miner) setDefaults() error { return err } - if mnr.relayDifficultyBits == 0 { - mnr.relayDifficultyBits = params.GetMinRelayDifficultyBits() + if len(mnr.relayDifficultyTargetHash) == 0 { + mnr.relayDifficultyTargetHash = params.GetRelayDifficultyTargetHash() } return nil } @@ -106,16 +109,15 @@ func (mnr *miner) mapMineRelay( _ context.Context, relay *servicetypes.Relay, ) (_ either.Either[*relayer.MinedRelay], skip bool) { - // TODO_TECHDEBT(@red-0ne, #446): Centralize the configuration for the SMT spec. - // TODO_TECHDEBT(@red-0ne): marshal using canonical codec. relayBz, err := relay.Marshal() if err != nil { return either.Error[*relayer.MinedRelay](err), false } - relayHash := servicetypes.GetHashFromBytes(relayBz) + relayHashArr := protocol.GetRelayHashFromBytes(relayBz) + relayHash := relayHashArr[:] // The relay IS NOT volume / reward applicable - if uint64(protocol.CountHashDifficultyBits(relayHash)) < mnr.relayDifficultyBits { + if !protocol.IsRelayVolumeApplicable(relayHash, mnr.relayDifficultyTargetHash) { return either.Success[*relayer.MinedRelay](nil), true } @@ -123,6 +125,6 @@ func (mnr *miner) mapMineRelay( return either.Success(&relayer.MinedRelay{ Relay: *relay, Bytes: relayBz, - Hash: relayHash[:], + Hash: relayHash, }), false } diff --git a/pkg/relayer/miner/miner_test.go b/pkg/relayer/miner/miner_test.go index 34fb99d43..3b817a075 100644 --- a/pkg/relayer/miner/miner_test.go +++ b/pkg/relayer/miner/miner_test.go @@ -15,6 +15,7 @@ import ( "cosmossdk.io/depinject" "github.com/stretchr/testify/require" + "github.com/pokt-network/poktroll/pkg/crypto/protocol" "github.com/pokt-network/poktroll/pkg/observable/channel" "github.com/pokt-network/poktroll/pkg/relayer" "github.com/pokt-network/poktroll/pkg/relayer/miner" @@ -22,11 +23,11 @@ import ( servicetypes "github.com/pokt-network/poktroll/x/service/types" ) -const testDifficulty = uint64(16) +var testRelayMiningTargetHash, _ = hex.DecodeString("0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") // TestMiner_MinedRelays constructs an observable of mined relays, through which // it pipes pre-mined relay fixtures. It asserts that the observable only emits -// mined relays with difficulty equal to or greater than testDifficulty. +// mined relays with difficulty equal to or greater than testTargetHash. // // To regenerate all fixtures, use `make go_testgen_fixtures`; to regenerate only this // test's fixtures run `go generate ./pkg/relayer/miner/miner_test.go`. @@ -42,7 +43,7 @@ func TestMiner_MinedRelays(t *testing.T) { proofQueryClientMock := testqueryclients.NewTestProofQueryClient(t) deps := depinject.Supply(proofQueryClientMock) - mnr, err := miner.NewMiner(deps, miner.WithDifficulty(testDifficulty)) + mnr, err := miner.NewMiner(deps, miner.WithRelayDifficultyTargetHash(testRelayMiningTargetHash)) require.NoError(t, err) minedRelays := mnr.MinedRelays(ctx, mockRelaysObs) @@ -133,8 +134,7 @@ func unmarshalHexMinedRelay( err = relay.Unmarshal(relayBz) require.NoError(t, err) - // TODO_TECHDEBT(@red-0ne, #446): Centralize the configuration for the SMT spec. - relayHashArr := servicetypes.GetHashFromBytes(relayBz) + relayHashArr := protocol.GetRelayHashFromBytes(relayBz) relayHash := relayHashArr[:] return &relayer.MinedRelay{ diff --git a/pkg/relayer/miner/options.go b/pkg/relayer/miner/options.go index da87dc27f..ebee97c40 100644 --- a/pkg/relayer/miner/options.go +++ b/pkg/relayer/miner/options.go @@ -2,10 +2,9 @@ package miner import "github.com/pokt-network/poktroll/pkg/relayer" -// WithDifficulty sets the difficulty of the miner, where difficultyBytes is the -// minimum number of leading zero bytes. -func WithDifficulty(difficultyBits uint64) relayer.MinerOption { +// WithRelayDifficultyTargetHash sets the relayDifficultyTargetHash of the miner. +func WithRelayDifficultyTargetHash(targetHash []byte) relayer.MinerOption { return func(mnr relayer.Miner) { - mnr.(*miner).relayDifficultyBits = difficultyBits + mnr.(*miner).relayDifficultyTargetHash = targetHash } } diff --git a/proto/poktroll/proof/params.proto b/proto/poktroll/proof/params.proto index 0183990c6..8f8042d95 100644 --- a/proto/poktroll/proof/params.proto +++ b/proto/poktroll/proof/params.proto @@ -12,9 +12,9 @@ message Params { option (amino.name) = "poktroll/x/proof/Params"; option (gogoproto.equal) = true; - // min_relay_difficulty_bits is the minimum difficulty in bits for a relay to - // be included in a Merkle proof. - uint64 min_relay_difficulty_bits = 1 [(gogoproto.jsontag) = "min_relay_difficulty_bits"]; + // TODO_FOLLOWUP(@olshansk, #690): Either delete this or change it to be named "minimum" + // relay_difficulty_target_hash is the maximum value a relay hash must be less than to be volume/reward applicable. + bytes relay_difficulty_target_hash = 1 [(gogoproto.jsontag) = "relay_difficulty_target_hash"]; // proof_request_probability is the probability of a session requiring a proof // if it's cost (i.e. compute unit consumption) is below the ProofRequirementThreshold. diff --git a/telemetry/event_counters.go b/telemetry/event_counters.go index 9b95af5b5..c8fb2f456 100644 --- a/telemetry/event_counters.go +++ b/telemetry/event_counters.go @@ -147,10 +147,10 @@ func ClaimCounter( ) } -// RelayMiningDifficultyGauge sets a gauge which tracks the relay mining difficulty, -// which is represented by number of leading zero bits. -// The serviceId is used as a label to be able to track the difficulty for each service. -func RelayMiningDifficultyGauge(numbLeadingZeroBits int, serviceId string) { +// RelayMiningDifficultyGauge sets a gauge which tracks the integer representation +// of the relay mining difficulty. The serviceId is used as a label to be able to +// track the difficulty for each service. +func RelayMiningDifficultyGauge(difficulty int64, serviceId string) { labels := []metrics.Label{ {Name: "type", Value: "relay_mining_difficulty"}, {Name: "service_id", Value: serviceId}, @@ -158,7 +158,7 @@ func RelayMiningDifficultyGauge(numbLeadingZeroBits int, serviceId string) { telemetry.SetGaugeWithLabels( []string{eventTypeMetricKeyGauge}, - float32(numbLeadingZeroBits), + float32(difficulty), labels, ) } diff --git a/tests/integration/tokenomics/relay_mining_difficulty_test.go b/tests/integration/tokenomics/relay_mining_difficulty_test.go index 09430a7b1..3bc9a82c5 100644 --- a/tests/integration/tokenomics/relay_mining_difficulty_test.go +++ b/tests/integration/tokenomics/relay_mining_difficulty_test.go @@ -21,8 +21,6 @@ import ( tokenomicstypes "github.com/pokt-network/poktroll/x/tokenomics/types" ) -// TODO_UPNEXT(@Olshansk, #571): Implement these tests - func init() { cmd.InitSDKConfig() } @@ -119,8 +117,8 @@ func TestUpdateRelayMiningDifficulty_NewServiceSeenForTheFirstTime(t *testing.T) relayMiningEvent := relayMiningEvents[0] require.Equal(t, "svc1", relayMiningEvent.ServiceId) // The default difficulty) - require.Equal(t, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", relayMiningEvent.PrevTargetHashHexEncoded) - require.Equal(t, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", relayMiningEvent.NewTargetHashHexEncoded) + require.Equal(t, prooftypes.DefaultRelayDifficultyTargetHashHex, relayMiningEvent.PrevTargetHashHexEncoded) + require.Equal(t, prooftypes.DefaultRelayDifficultyTargetHashHex, relayMiningEvent.NewTargetHashHexEncoded) // The previous EMA is the same as the current one if the service is new require.Equal(t, uint64(1), relayMiningEvent.PrevNumRelaysEma) require.Equal(t, uint64(1), relayMiningEvent.NewNumRelaysEma) diff --git a/testutil/network/network.go b/testutil/network/network.go index fdfb7f8a1..091f871b7 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -50,7 +50,7 @@ func New(t *testing.T, configs ...Config) *Network { cfg = configs[0] } net, err := network.New(t, t.TempDir(), cfg) - require.NoError(t, err) + require.NoError(t, err, "TODO_FLAKY: This config setup is periodically flaky") _, err = net.WaitForHeight(1) require.NoError(t, err) t.Cleanup(net.Cleanup) diff --git a/testutil/testrelayer/relays.go b/testutil/testrelayer/relays.go index ff2dfb54f..43a4c5753 100644 --- a/testutil/testrelayer/relays.go +++ b/testutil/testrelayer/relays.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/pokt-network/poktroll/pkg/crypto" + "github.com/pokt-network/poktroll/pkg/crypto/protocol" "github.com/pokt-network/poktroll/pkg/relayer" testutilkeyring "github.com/pokt-network/poktroll/testutil/testkeyring" servicetypes "github.com/pokt-network/poktroll/x/service/types" @@ -52,11 +53,10 @@ func NewUnsignedMinedRelay( }, } - // TODO_TECHDEBT(@red-0ne): marshal using canonical codec. relayBz, err := relay.Marshal() require.NoError(t, err) - relayHashArr := servicetypes.GetHashFromBytes(relayBz) + relayHashArr := protocol.GetRelayHashFromBytes(relayBz) relayHash := relayHashArr[:] return &relayer.MinedRelay{ @@ -110,7 +110,7 @@ func NewSignedMinedRelay( relayBz, err := relay.Marshal() require.NoError(t, err) - relayHashArr := servicetypes.GetHashFromBytes(relayBz) + relayHashArr := protocol.GetRelayHashFromBytes(relayBz) relayHash := relayHashArr[:] return &relayer.MinedRelay{ diff --git a/x/proof/keeper/msg_server_submit_proof.go b/x/proof/keeper/msg_server_submit_proof.go index e462022b0..fcc271b03 100644 --- a/x/proof/keeper/msg_server_submit_proof.go +++ b/x/proof/keeper/msg_server_submit_proof.go @@ -204,10 +204,15 @@ func (k msgServer) SubmitProof( logger.Debug("successfully verified relay response signature") // Get the proof module's governance parameters. + // TODO_FOLLOWUP(@olshansk, #690): Get the difficulty associated with the service params := k.GetParams(ctx) // Verify the relay difficulty is above the minimum required to earn rewards. - if err = validateMiningDifficulty(relayBz, params.MinRelayDifficultyBits); err != nil { + if err = validateRelayDifficulty( + relayBz, + params.RelayDifficultyTargetHash, + sessionHeader.Service.Id, + ); err != nil { return nil, status.Error(codes.FailedPrecondition, err.Error()) } logger.Debug("successfully validated relay mining difficulty") @@ -224,11 +229,6 @@ func (k msgServer) SubmitProof( } logger.Debug("successfully validated proof path") - // Verify the relay's difficulty. - if err = validateMiningDifficulty(relayBz, params.MinRelayDifficultyBits); err != nil { - return nil, status.Error(codes.FailedPrecondition, err.Error()) - } - // Retrieve the corresponding claim for the proof submitted so it can be // used in the proof validation below. claim, err = k.queryAndValidateClaimForProof(ctx, msg) @@ -447,21 +447,35 @@ func verifyClosestProof( return nil } -// validateMiningDifficulty ensures that the relay's mining difficulty meets the +// validateRelayDifficulty ensures that the relay's mining difficulty meets the // required minimum threshold. // TODO_TECHDEBT: Factor out the relay mining difficulty validation into a shared // function that can be used by both the proof and the miner packages. -func validateMiningDifficulty(relayBz []byte, minRelayDifficultyBits uint64) error { - relayHash := servicetypes.GetHashFromBytes(relayBz) - relayDifficultyBits := protocol.CountHashDifficultyBits(relayHash) +func validateRelayDifficulty(relayBz, targetHash []byte, serviceId string) error { + relayHashArr := protocol.GetRelayHashFromBytes(relayBz) + relayHash := relayHashArr[:] + + if len(targetHash) != protocol.RelayHasherSize { + return types.ErrProofInvalidRelay.Wrapf( + "invalid RelayDifficultyTargetHash: (%x); length wanted: %d; got: %d", + targetHash, + protocol.RelayHasherSize, + len(targetHash), + ) + } + + if !protocol.IsRelayVolumeApplicable(relayHash, targetHash) { + var targetHashArr [protocol.RelayHasherSize]byte + copy(targetHashArr[:], targetHash) + + relayDifficulty := protocol.GetDifficultyFromHash(relayHashArr) + targetDifficulty := protocol.GetDifficultyFromHash(targetHashArr) - // TODO_MAINNET: Devise a test that tries to attack the network and ensure that there - // is sufficient telemetry. - if uint64(relayDifficultyBits) < minRelayDifficultyBits { return types.ErrProofInvalidRelay.Wrapf( - "relay difficulty %d is less than the minimum difficulty %d", - relayDifficultyBits, - minRelayDifficultyBits, + "the difficulty relay being proven is (%d), and is smaller than the target difficulty (%d) for service %s", + relayDifficulty, + targetDifficulty, + serviceId, ) } diff --git a/x/proof/keeper/msg_server_submit_proof_test.go b/x/proof/keeper/msg_server_submit_proof_test.go index c66c27d11..99d890294 100644 --- a/x/proof/keeper/msg_server_submit_proof_test.go +++ b/x/proof/keeper/msg_server_submit_proof_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "context" + "encoding/hex" "os" "testing" @@ -44,11 +45,11 @@ var ( expectedMerkleProofPath []byte // testProofParams sets: - // - the minimum relay difficulty bits to zero so that these tests don't need to mine for valid relays. + // - the relay difficulty target hash to the easiest difficulty so that these tests don't need to mine for valid relays. // - the proof request probability to 1 so that all test sessions require a proof. testProofParams = prooftypes.Params{ - MinRelayDifficultyBits: 0, - ProofRequestProbability: 1, + RelayDifficultyTargetHash: protocol.BaseRelayDifficultyHashBz, + ProofRequestProbability: 1, } ) @@ -528,7 +529,7 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) { ) // Compute the difficulty in bits of the closest relay from the valid session tree. - validClosestRelayDifficultyBits := getClosestRelayDifficultyBits(t, validSessionTree, expectedMerkleProofPath) + validClosestRelayDifficultyBits := getClosestRelayDifficulty(t, validSessionTree, expectedMerkleProofPath) // Copy `emptyBlockHash` to `wrongClosestProofPath` to with a missing byte // so the closest proof is invalid (i.e. unmarshalable). @@ -565,6 +566,11 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) { copy(wrongClosestProofPath, expectedMerkleProofPath) copy(wrongClosestProofPath, "wrong closest proof path") + lowTargetHash, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000ff") + var lowTargetHashArr [protocol.RelayHasherSize]byte + copy(lowTargetHashArr[:], lowTargetHash) + highExpectedTargetDifficulty := protocol.GetDifficultyFromHash(lowTargetHashArr) + tests := []struct { desc string newProofMsg func(t *testing.T) *prooftypes.MsgSubmitProof @@ -1017,9 +1023,9 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) { desc: "relay difficulty must be greater than or equal to minimum (zero difficulty)", newProofMsg: func(t *testing.T) *prooftypes.MsgSubmitProof { // Set the minimum relay difficulty to a non-zero value such that the relays - // constructed by the test helpers have a negligable chance of being valid. + // constructed by the test helpers have a negligible chance of being valid. err = keepers.Keeper.SetParams(ctx, prooftypes.Params{ - MinRelayDifficultyBits: 10, + RelayDifficultyTargetHash: lowTargetHash, }) require.NoError(t, err) @@ -1041,9 +1047,10 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) { expectedErr: status.Error( codes.FailedPrecondition, prooftypes.ErrProofInvalidRelay.Wrapf( - "relay difficulty %d is less than the minimum difficulty %d", + "the difficulty relay being proven is (%d), and is smaller than the target difficulty (%d) for service %s", validClosestRelayDifficultyBits, - 10, + highExpectedTargetDifficulty, + validSessionHeader.Service.Id, ).Error(), ), }, @@ -1400,14 +1407,14 @@ func createClaimAndStoreBlockHash( return claimRes.GetClaim() } -// getClosestRelayDifficultyBits returns the number of leading 0s (i.e. relay -// mining difficulty bits) in the relayHash stored in the sessionTree that is -// is closest to the merkle proof path provided. -func getClosestRelayDifficultyBits( +// getClosestRelayDifficulty returns the mining difficulty number which corresponds +// to the relayHash stored in the sessionTree that is closest to the merkle proof +// path provided. +func getClosestRelayDifficulty( t *testing.T, sessionTree relayer.SessionTree, closestMerkleProofPath []byte, -) uint64 { +) int64 { // Retrieve a merkle proof that is closest to the path provided closestMerkleProof, err := sessionTree.ProveClosest(closestMerkleProofPath) require.NoError(t, err) @@ -1422,6 +1429,5 @@ func getClosestRelayDifficultyBits( relayHash, err := relay.GetHash() require.NoError(t, err) - // Count the number of leading 0s in the relay hash to determine its difficulty. - return uint64(protocol.CountHashDifficultyBits(relayHash)) + return protocol.GetDifficultyFromHash(relayHash) } diff --git a/x/proof/keeper/msg_server_update_param.go b/x/proof/keeper/msg_server_update_param.go index 7a1faa50b..6b941068d 100644 --- a/x/proof/keeper/msg_server_update_param.go +++ b/x/proof/keeper/msg_server_update_param.go @@ -23,18 +23,18 @@ func (k msgServer) UpdateParam( params := k.GetParams(ctx) switch msg.Name { - case types.ParamMinRelayDifficultyBits: - value, ok := msg.AsType.(*types.MsgUpdateParam_AsInt64) + case types.ParamRelayDifficultyTargetHash: + value, ok := msg.AsType.(*types.MsgUpdateParam_AsBytes) if !ok { return nil, types.ErrProofParamInvalid.Wrapf("unsupported value type for %s param: %T", msg.Name, msg.AsType) } - minRelayDifficultyBits := uint64(value.AsInt64) + relayDifficultyTargetHash := value.AsBytes - if err := types.ValidateMinRelayDifficultyBits(minRelayDifficultyBits); err != nil { + if err := types.ValidateRelayDifficultyTargetHash(relayDifficultyTargetHash); err != nil { return nil, err } - params.MinRelayDifficultyBits = minRelayDifficultyBits + params.RelayDifficultyTargetHash = relayDifficultyTargetHash case types.ParamProofRequestProbability: value, ok := msg.AsType.(*types.MsgUpdateParam_AsFloat) if !ok { diff --git a/x/proof/keeper/msg_server_update_param_test.go b/x/proof/keeper/msg_server_update_param_test.go index 743e2ea8d..243de80ea 100644 --- a/x/proof/keeper/msg_server_update_param_test.go +++ b/x/proof/keeper/msg_server_update_param_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "encoding/hex" "testing" "cosmossdk.io/math" @@ -15,7 +16,7 @@ import ( ) func TestMsgUpdateParam_UpdateMinRelayDifficultyBitsOnly(t *testing.T) { - var expectedMinRelayDifficultyBits uint64 = 8 + expectedRelayDifficultyTargetHash, _ := hex.DecodeString("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff") // Set the parameters to their default values k, msgSrv, ctx := setupMsgServer(t) @@ -23,22 +24,22 @@ func TestMsgUpdateParam_UpdateMinRelayDifficultyBitsOnly(t *testing.T) { require.NoError(t, k.SetParams(ctx, defaultParams)) // Ensure the default values are different from the new values we want to set - require.NotEqual(t, expectedMinRelayDifficultyBits, defaultParams.MinRelayDifficultyBits) + require.NotEqual(t, expectedRelayDifficultyTargetHash, defaultParams.RelayDifficultyTargetHash) // Update the min relay difficulty bits updateParamMsg := &prooftypes.MsgUpdateParam{ Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Name: prooftypes.ParamMinRelayDifficultyBits, - AsType: &prooftypes.MsgUpdateParam_AsInt64{AsInt64: int64(expectedMinRelayDifficultyBits)}, + Name: prooftypes.ParamRelayDifficultyTargetHash, + AsType: &prooftypes.MsgUpdateParam_AsBytes{AsBytes: expectedRelayDifficultyTargetHash}, } res, err := msgSrv.UpdateParam(ctx, updateParamMsg) require.NoError(t, err) - require.NotEqual(t, defaultParams.MinRelayDifficultyBits, res.Params.MinRelayDifficultyBits) - require.Equal(t, expectedMinRelayDifficultyBits, res.Params.MinRelayDifficultyBits) + require.NotEqual(t, defaultParams.RelayDifficultyTargetHash, res.Params.RelayDifficultyTargetHash) + require.Equal(t, expectedRelayDifficultyTargetHash, res.Params.RelayDifficultyTargetHash) // Ensure the other parameters are unchanged - testkeeper.AssertDefaultParamsEqualExceptFields(t, &defaultParams, res.Params, "MinRelayDifficultyBits") + testkeeper.AssertDefaultParamsEqualExceptFields(t, &defaultParams, res.Params, "RelayDifficultyTargetHash") } func TestMsgUpdateParam_UpdateProofRequestProbabilityOnly(t *testing.T) { diff --git a/x/proof/keeper/msg_update_params_test.go b/x/proof/keeper/msg_update_params_test.go index 5e2a528a6..f5ce27e29 100644 --- a/x/proof/keeper/msg_update_params_test.go +++ b/x/proof/keeper/msg_update_params_test.go @@ -42,7 +42,8 @@ func TestMsgUpdateParams(t *testing.T) { params: &types.MsgUpdateParams{ Authority: k.GetAuthority(), Params: types.Params{ - ProofMissingPenalty: &types.DefaultProofMissingPenalty, + ProofMissingPenalty: &types.DefaultProofMissingPenalty, + RelayDifficultyTargetHash: types.DefaultRelayDifficultyTargetHash, }, }, shouldError: false, diff --git a/x/proof/keeper/params_test.go b/x/proof/keeper/params_test.go index 1d7c92945..6c281b639 100644 --- a/x/proof/keeper/params_test.go +++ b/x/proof/keeper/params_test.go @@ -20,24 +20,24 @@ func TestGetParams(t *testing.T) { } func TestParams_ValidateMinRelayDifficulty(t *testing.T) { tests := []struct { - desc string - minRelayDifficultyBits any - expectedErr error + desc string + relayDifficultyTargetHash any + expectedErr error }{ { - desc: "invalid type", - minRelayDifficultyBits: int64(-1), - expectedErr: prooftypes.ErrProofParamInvalid.Wrapf("invalid parameter type: int64"), + desc: "invalid type", + relayDifficultyTargetHash: int64(-1), + expectedErr: prooftypes.ErrProofParamInvalid.Wrapf("invalid parameter type: int64"), }, { - desc: "valid MinRelayDifficultyBits", - minRelayDifficultyBits: uint64(4), + desc: "valid RelayDifficultyTargetHash", + relayDifficultyTargetHash: prooftypes.DefaultRelayDifficultyTargetHash, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - err := prooftypes.ValidateMinRelayDifficultyBits(tt.minRelayDifficultyBits) + err := prooftypes.ValidateRelayDifficultyTargetHash(tt.relayDifficultyTargetHash) if tt.expectedErr != nil { require.Error(t, err) require.Contains(t, err.Error(), tt.expectedErr.Error()) diff --git a/x/proof/types/message_update_param.go b/x/proof/types/message_update_param.go index c2271453b..b9a0b4621 100644 --- a/x/proof/types/message_update_param.go +++ b/x/proof/types/message_update_param.go @@ -47,8 +47,8 @@ func (msg *MsgUpdateParam) ValidateBasic() error { // Parameter name must be supported by this module. switch msg.Name { - case ParamMinRelayDifficultyBits: - return msg.paramTypeIsInt64() + case ParamRelayDifficultyTargetHash: + return msg.paramTypeIsBytes() case ParamProofRequestProbability: return msg.paramTypeIsFloat() case ParamProofRequirementThreshold: @@ -60,6 +60,18 @@ func (msg *MsgUpdateParam) ValidateBasic() error { } } +// paramTypeIsBytes checks if the parameter type is a byte slice, returning an error if not. +func (msg *MsgUpdateParam) paramTypeIsBytes() error { + if _, ok := msg.AsType.(*MsgUpdateParam_AsBytes); !ok { + return ErrProofParamInvalid.Wrapf( + "invalid type for param %q expected %T, got %T", + msg.Name, &MsgUpdateParam_AsBytes{}, + msg.AsType, + ) + } + return nil +} + // paramTypeIsInt64 checks if the parameter type is int64, returning an error if not. func (msg *MsgUpdateParam) paramTypeIsInt64() error { if _, ok := msg.AsType.(*MsgUpdateParam_AsInt64); !ok { diff --git a/x/proof/types/message_update_param_test.go b/x/proof/types/message_update_param_test.go index 1fd4793a8..d89a323ad 100644 --- a/x/proof/types/message_update_param_test.go +++ b/x/proof/types/message_update_param_test.go @@ -28,7 +28,7 @@ func TestMsgUpdateParam_ValidateBasic(t *testing.T) { name: "invalid: param name incorrect (non-existent)", msg: MsgUpdateParam{ Authority: sample.AccAddress(), - Name: "WRONG_min_relay_difficulty_bits", + Name: "WRONG_relay_difficulty_target_hash", AsType: &MsgUpdateParam_AsInt64{AsInt64: 1}, }, @@ -37,7 +37,7 @@ func TestMsgUpdateParam_ValidateBasic(t *testing.T) { name: "invalid: incorrect param type", msg: MsgUpdateParam{ Authority: sample.AccAddress(), - Name: ParamMinRelayDifficultyBits, + Name: ParamRelayDifficultyTargetHash, AsType: &MsgUpdateParam_AsString{AsString: "invalid"}, }, expectedErr: ErrProofParamInvalid, @@ -45,8 +45,8 @@ func TestMsgUpdateParam_ValidateBasic(t *testing.T) { name: "valid: correct authority, param name, and type", msg: MsgUpdateParam{ Authority: sample.AccAddress(), - Name: ParamMinRelayDifficultyBits, - AsType: &MsgUpdateParam_AsInt64{AsInt64: 1}, + Name: ParamRelayDifficultyTargetHash, + AsType: &MsgUpdateParam_AsBytes{AsBytes: DefaultRelayDifficultyTargetHash}, }, expectedErr: nil, diff --git a/x/proof/types/params.go b/x/proof/types/params.go index baed0ad3b..218831ba2 100644 --- a/x/proof/types/params.go +++ b/x/proof/types/params.go @@ -1,30 +1,38 @@ package types import ( + "encoding/hex" + "cosmossdk.io/math" cosmostypes "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/pokt-network/poktroll/app/volatile" "github.com/pokt-network/poktroll/pkg/client" + "github.com/pokt-network/poktroll/pkg/crypto/protocol" ) var ( _ client.ProofParams = (*Params)(nil) _ paramtypes.ParamSet = (*Params)(nil) - KeyMinRelayDifficultyBits = []byte("MinRelayDifficultyBits") - ParamMinRelayDifficultyBits = "min_relay_difficulty_bits" - DefaultMinRelayDifficultyBits uint64 = 0 // TODO_MAINNET(#142, #401): Determine the default value. - KeyProofRequestProbability = []byte("ProofRequestProbability") - ParamProofRequestProbability = "proof_request_probability" - DefaultProofRequestProbability float32 = 0.25 // See: https://github.com/pokt-network/pocket-core/blob/staging/docs/proposals/probabilistic_proofs.md - KeyProofRequirementThreshold = []byte("ProofRequirementThreshold") - ParamProofRequirementThreshold = "proof_requirement_threshold" - DefaultProofRequirementThreshold uint64 = 20 // See: https://github.com/pokt-network/pocket-core/blob/staging/docs/proposals/probabilistic_proofs.md - KeyProofMissingPenalty = []byte("ProofMissingPenalty") - ParamProofMissingPenalty = "proof_missing_penalty" - DefaultProofMissingPenalty = cosmostypes.NewCoin(volatile.DenomuPOKT, math.NewInt(320)) // See: https://github.com/pokt-network/pocket-core/blob/staging/docs/proposals/probabilistic_proofs.md + // TODO_FOLLOWUP(@olshansk, #690): Delete this parameter. + KeyRelayDifficultyTargetHash = []byte("RelayDifficultyTargetHash") + ParamRelayDifficultyTargetHash = "relay_difficulty_target_hash" + DefaultRelayDifficultyTargetHashHex = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" // all relays are payable + DefaultRelayDifficultyTargetHash, _ = hex.DecodeString(DefaultRelayDifficultyTargetHashHex) + + KeyProofRequestProbability = []byte("ProofRequestProbability") + ParamProofRequestProbability = "proof_request_probability" + DefaultProofRequestProbability float32 = 0.25 // See: https://github.com/pokt-network/pocket-core/blob/staging/docs/proposals/probabilistic_proofs.md + + KeyProofRequirementThreshold = []byte("ProofRequirementThreshold") + ParamProofRequirementThreshold = "proof_requirement_threshold" + DefaultProofRequirementThreshold uint64 = 20 // See: https://github.com/pokt-network/pocket-core/blob/staging/docs/proposals/probabilistic_proofs.md + + KeyProofMissingPenalty = []byte("ProofMissingPenalty") + ParamProofMissingPenalty = "proof_missing_penalty" + DefaultProofMissingPenalty = cosmostypes.NewCoin(volatile.DenomuPOKT, math.NewInt(320)) // See: https://github.com/pokt-network/pocket-core/blob/staging/docs/proposals/probabilistic_proofs.md ) // ParamKeyTable the param key table for launch module @@ -34,13 +42,13 @@ func ParamKeyTable() paramtypes.KeyTable { // NewParams creates a new Params instance func NewParams( - minRelayDifficultyBits uint64, + relayDifficultyTargetHash []byte, proofRequestProbability float32, proofRequirementThreshold uint64, proofMissingPenalty *cosmostypes.Coin, ) Params { return Params{ - MinRelayDifficultyBits: minRelayDifficultyBits, + RelayDifficultyTargetHash: relayDifficultyTargetHash, ProofRequestProbability: proofRequestProbability, ProofRequirementThreshold: proofRequirementThreshold, ProofMissingPenalty: proofMissingPenalty, @@ -50,7 +58,7 @@ func NewParams( // DefaultParams returns a default set of parameters func DefaultParams() Params { return NewParams( - DefaultMinRelayDifficultyBits, + DefaultRelayDifficultyTargetHash, DefaultProofRequestProbability, DefaultProofRequirementThreshold, &DefaultProofMissingPenalty, @@ -61,9 +69,9 @@ func DefaultParams() Params { func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ paramtypes.NewParamSetPair( - KeyMinRelayDifficultyBits, - &p.MinRelayDifficultyBits, - ValidateMinRelayDifficultyBits, + KeyRelayDifficultyTargetHash, + &p.RelayDifficultyTargetHash, + ValidateRelayDifficultyTargetHash, ), paramtypes.NewParamSetPair( KeyProofRequestProbability, @@ -86,7 +94,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { // ValidateBasic does a sanity check on the provided params. func (params *Params) ValidateBasic() error { // Validate the ComputeUnitsToTokensMultiplier - if err := ValidateMinRelayDifficultyBits(params.MinRelayDifficultyBits); err != nil { + if err := ValidateRelayDifficultyTargetHash(params.RelayDifficultyTargetHash); err != nil { return err } @@ -105,13 +113,23 @@ func (params *Params) ValidateBasic() error { return nil } -// ValidateMinRelayDifficultyBits validates the MinRelayDifficultyBits param. +// ValidateRelayDifficultyTargetHash validates the MinRelayDifficultyBits param. // NB: The argument is an interface type to satisfy the ParamSetPair function signature. -func ValidateMinRelayDifficultyBits(v interface{}) error { - if _, ok := v.(uint64); !ok { +func ValidateRelayDifficultyTargetHash(v interface{}) error { + targetHash, ok := v.([]byte) + if !ok { return ErrProofParamInvalid.Wrapf("invalid parameter type: %T", v) } + if len(targetHash) != protocol.RelayHasherSize { + return ErrProofParamInvalid.Wrapf( + "invalid RelayDifficultyTargetHash: (%x); length wanted: %d; got: %d", + targetHash, + 32, + len(targetHash), + ) + } + return nil } diff --git a/x/proof/types/params.pb.go b/x/proof/types/params.pb.go index 209a20f7a..a82615004 100644 --- a/x/proof/types/params.pb.go +++ b/x/proof/types/params.pb.go @@ -4,6 +4,7 @@ package types import ( + bytes "bytes" encoding_binary "encoding/binary" fmt "fmt" types "github.com/cosmos/cosmos-sdk/types" @@ -28,9 +29,9 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Params defines the parameters for the module. type Params struct { - // min_relay_difficulty_bits is the minimum difficulty in bits for a relay to - // be included in a Merkle proof. - MinRelayDifficultyBits uint64 `protobuf:"varint,1,opt,name=min_relay_difficulty_bits,json=minRelayDifficultyBits,proto3" json:"min_relay_difficulty_bits"` + // TODO_FOLLOWUP(@olshansk, #690): Either delete this or change it to be named "minimum" + // relay_difficulty_target_hash is the maximum value a relay hash must be less than to be volume/reward applicable. + RelayDifficultyTargetHash []byte `protobuf:"bytes,1,opt,name=relay_difficulty_target_hash,json=relayDifficultyTargetHash,proto3" json:"relay_difficulty_target_hash"` // proof_request_probability is the probability of a session requiring a proof // if it's cost (i.e. compute unit consumption) is below the ProofRequirementThreshold. ProofRequestProbability float32 `protobuf:"fixed32,2,opt,name=proof_request_probability,json=proofRequestProbability,proto3" json:"proof_request_probability"` @@ -80,11 +81,11 @@ func (m *Params) XXX_DiscardUnknown() { var xxx_messageInfo_Params proto.InternalMessageInfo -func (m *Params) GetMinRelayDifficultyBits() uint64 { +func (m *Params) GetRelayDifficultyTargetHash() []byte { if m != nil { - return m.MinRelayDifficultyBits + return m.RelayDifficultyTargetHash } - return 0 + return nil } func (m *Params) GetProofRequestProbability() float32 { @@ -115,32 +116,33 @@ func init() { func init() { proto.RegisterFile("poktroll/proof/params.proto", fileDescriptor_2ad689ad5bf3a2d7) } var fileDescriptor_2ad689ad5bf3a2d7 = []byte{ - // 399 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xbf, 0x8e, 0xd4, 0x30, - 0x10, 0xc6, 0xd7, 0x77, 0xa7, 0x2b, 0x82, 0x84, 0x44, 0xf8, 0x73, 0xc9, 0x9d, 0x48, 0x22, 0xaa, - 0x15, 0x12, 0xb6, 0x0e, 0x3a, 0xca, 0x40, 0x41, 0x83, 0xb4, 0x8a, 0x28, 0x80, 0xc6, 0x72, 0x72, - 0xde, 0xc4, 0xba, 0xd8, 0x13, 0x6c, 0x2f, 0x90, 0x57, 0xa0, 0xe2, 0x11, 0xa8, 0xa9, 0x78, 0x0c, - 0xca, 0x2b, 0xa9, 0x56, 0x68, 0xb7, 0x00, 0xed, 0x53, 0xa0, 0xd8, 0xb9, 0xdd, 0x02, 0x96, 0x26, - 0xb2, 0xbe, 0xdf, 0xf7, 0x4d, 0x46, 0x33, 0x13, 0x9c, 0x75, 0x70, 0x69, 0x35, 0xb4, 0x2d, 0xe9, - 0x34, 0xc0, 0x9c, 0x74, 0x4c, 0x33, 0x69, 0x70, 0xa7, 0xc1, 0x42, 0x78, 0xf3, 0x1a, 0x62, 0x07, - 0x4f, 0x6f, 0x31, 0x29, 0x14, 0x10, 0xf7, 0xf5, 0x96, 0xd3, 0x3b, 0x35, 0xd4, 0xe0, 0x9e, 0x64, - 0x78, 0x8d, 0x6a, 0x52, 0x81, 0x91, 0x60, 0x48, 0xc9, 0x0c, 0x27, 0xef, 0xcf, 0x4b, 0x6e, 0xd9, - 0x39, 0xa9, 0x40, 0x28, 0xcf, 0x1f, 0x7c, 0x3d, 0x0c, 0x8e, 0x67, 0xee, 0x4f, 0xe1, 0xeb, 0x20, - 0x96, 0x42, 0x51, 0xcd, 0x5b, 0xd6, 0xd3, 0x0b, 0x31, 0x9f, 0x8b, 0x6a, 0xd1, 0xda, 0x9e, 0x96, - 0xc2, 0x9a, 0x08, 0x65, 0x68, 0x7a, 0x94, 0xdf, 0xdf, 0x2c, 0xd3, 0xfd, 0xa6, 0xe2, 0x9e, 0x14, - 0xaa, 0x18, 0xc8, 0xf3, 0x2d, 0xc8, 0x85, 0x35, 0xe1, 0x9b, 0x20, 0x76, 0x6d, 0x53, 0xcd, 0xdf, - 0x2d, 0xb8, 0xb1, 0xb4, 0xd3, 0x50, 0xb2, 0x52, 0xb4, 0xc2, 0xf6, 0xd1, 0x41, 0x86, 0xa6, 0x07, - 0xbe, 0xf2, 0x5e, 0x53, 0x71, 0xe2, 0x50, 0xe1, 0xc9, 0x6c, 0x07, 0x42, 0x1a, 0x9c, 0xed, 0x52, - 0x42, 0x73, 0xc9, 0x95, 0xa5, 0xb6, 0xd1, 0xdc, 0x34, 0xd0, 0x5e, 0x44, 0x87, 0xae, 0xed, 0x74, - 0xb3, 0x4c, 0xff, 0x67, 0x2b, 0xe2, 0x6d, 0xf9, 0x91, 0xbd, 0xba, 0x46, 0x61, 0x13, 0xdc, 0xf5, - 0x49, 0x29, 0x8c, 0x11, 0xaa, 0xa6, 0x1d, 0x57, 0xac, 0xb5, 0x7d, 0x74, 0x94, 0xa1, 0xe9, 0x8d, - 0xc7, 0x31, 0xf6, 0x03, 0xc6, 0xc3, 0x80, 0xf1, 0x38, 0x60, 0xfc, 0x0c, 0x84, 0xca, 0xe3, 0xcd, - 0x32, 0xfd, 0x77, 0xb6, 0xb8, 0xed, 0xe4, 0x97, 0x5e, 0x9d, 0x79, 0xf1, 0x69, 0xf6, 0xfb, 0x4b, - 0x8a, 0x3e, 0xfd, 0xfa, 0xf6, 0xf0, 0x64, 0x7b, 0x09, 0x1f, 0xc7, 0x5b, 0xf0, 0x1b, 0xca, 0x5f, - 0x7c, 0x5f, 0x25, 0xe8, 0x6a, 0x95, 0xa0, 0x9f, 0xab, 0x04, 0x7d, 0x5e, 0x27, 0x93, 0xab, 0x75, - 0x32, 0xf9, 0xb1, 0x4e, 0x26, 0x6f, 0x71, 0x2d, 0x6c, 0xb3, 0x28, 0x71, 0x05, 0x92, 0x0c, 0xe9, - 0x47, 0x8a, 0xdb, 0x0f, 0xa0, 0x2f, 0xc9, 0x5f, 0xa5, 0x6c, 0xdf, 0x71, 0x53, 0x1e, 0xbb, 0xed, - 0x3f, 0xf9, 0x13, 0x00, 0x00, 0xff, 0xff, 0x22, 0xfc, 0x96, 0xb6, 0x75, 0x02, 0x00, 0x00, + // 405 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x31, 0x8e, 0xd4, 0x30, + 0x14, 0x86, 0xc7, 0xbb, 0xab, 0x2d, 0x02, 0x42, 0x22, 0x80, 0x36, 0xd9, 0x85, 0x24, 0xa2, 0x8a, + 0x90, 0xb0, 0xb5, 0xd0, 0x51, 0x06, 0x8a, 0x6d, 0x90, 0x46, 0xd1, 0x36, 0xd0, 0x58, 0x4e, 0xd6, + 0x93, 0x58, 0x93, 0xe4, 0x05, 0xdb, 0x03, 0xe4, 0x0a, 0x54, 0x1c, 0x81, 0x23, 0x20, 0x71, 0x09, + 0xca, 0x29, 0xa9, 0x22, 0x34, 0x53, 0x80, 0x72, 0x0a, 0x34, 0x76, 0x66, 0xa6, 0x00, 0xa6, 0xb1, + 0x9e, 0xfe, 0xef, 0xff, 0x9f, 0xad, 0xe7, 0xe7, 0x5c, 0xb4, 0x30, 0xd7, 0x12, 0xaa, 0x8a, 0xb4, + 0x12, 0x60, 0x46, 0x5a, 0x26, 0x59, 0xad, 0x70, 0x2b, 0x41, 0x83, 0x7b, 0x67, 0x0b, 0xb1, 0x81, + 0xe7, 0x77, 0x59, 0x2d, 0x1a, 0x20, 0xe6, 0xb4, 0x96, 0xf3, 0xfb, 0x05, 0x14, 0x60, 0x4a, 0xb2, + 0xa9, 0x46, 0x35, 0xc8, 0x41, 0xd5, 0xa0, 0x48, 0xc6, 0x14, 0x27, 0xef, 0x2f, 0x33, 0xae, 0xd9, + 0x25, 0xc9, 0x41, 0x34, 0x96, 0x3f, 0xfe, 0x76, 0xec, 0x9c, 0x4e, 0xcd, 0x4d, 0x2e, 0x73, 0x1e, + 0x4a, 0x5e, 0xb1, 0x8e, 0xde, 0x88, 0xd9, 0x4c, 0xe4, 0x8b, 0x4a, 0x77, 0x54, 0x33, 0x59, 0x70, + 0x4d, 0x4b, 0xa6, 0x4a, 0x0f, 0x45, 0x28, 0xbe, 0x9d, 0x44, 0x43, 0x1f, 0x1e, 0xf4, 0xa5, 0xbe, + 0xa1, 0xaf, 0x76, 0xf0, 0xda, 0xb0, 0x2b, 0xa6, 0x4a, 0xf7, 0x8d, 0xe3, 0x9b, 0xf7, 0x53, 0xc9, + 0xdf, 0x2d, 0xb8, 0xd2, 0xb4, 0x95, 0x90, 0xb1, 0x4c, 0x54, 0x42, 0x77, 0xde, 0x51, 0x84, 0xe2, + 0xa3, 0xe4, 0xd1, 0xd0, 0x87, 0xff, 0x37, 0xa5, 0x67, 0x06, 0xa5, 0x96, 0x4c, 0xf7, 0xc0, 0xa5, + 0xce, 0xc5, 0x3e, 0x25, 0x24, 0xaf, 0x79, 0xa3, 0xa9, 0x2e, 0x25, 0x57, 0x25, 0x54, 0x37, 0xde, + 0x71, 0x84, 0xe2, 0x93, 0x24, 0x1c, 0xfa, 0xf0, 0x90, 0x2d, 0xf5, 0x77, 0xed, 0x47, 0x76, 0xbd, + 0x45, 0x6e, 0xe9, 0x3c, 0xb0, 0xc9, 0x5a, 0x28, 0x25, 0x9a, 0x82, 0xb6, 0xbc, 0x61, 0x95, 0xee, + 0xbc, 0x93, 0x08, 0xc5, 0xb7, 0x9e, 0xf9, 0xd8, 0x4e, 0x1a, 0x6f, 0x26, 0x8d, 0xc7, 0x49, 0xe3, + 0x97, 0x20, 0x9a, 0xc4, 0x1f, 0xfa, 0xf0, 0xdf, 0xd9, 0xf4, 0x9e, 0x91, 0x5f, 0x5b, 0x75, 0x6a, + 0xc5, 0x17, 0xd1, 0xef, 0x2f, 0x21, 0xfa, 0xf4, 0xeb, 0xeb, 0x93, 0xb3, 0xdd, 0x4a, 0x7c, 0x1c, + 0x97, 0xc2, 0x7e, 0x55, 0x72, 0xf5, 0x7d, 0x15, 0xa0, 0xe5, 0x2a, 0x40, 0x3f, 0x57, 0x01, 0xfa, + 0xbc, 0x0e, 0x26, 0xcb, 0x75, 0x30, 0xf9, 0xb1, 0x0e, 0x26, 0x6f, 0x71, 0x21, 0x74, 0xb9, 0xc8, + 0x70, 0x0e, 0x35, 0xd9, 0xa4, 0x9f, 0x36, 0x5c, 0x7f, 0x00, 0x39, 0x27, 0x7f, 0xb5, 0xd2, 0x5d, + 0xcb, 0x55, 0x76, 0x6a, 0xd6, 0xe0, 0xf9, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xe3, 0x17, + 0x08, 0x7e, 0x02, 0x00, 0x00, } func (this *Params) Equal(that interface{}) bool { @@ -162,7 +164,7 @@ func (this *Params) Equal(that interface{}) bool { } else if this == nil { return false } - if this.MinRelayDifficultyBits != that1.MinRelayDifficultyBits { + if !bytes.Equal(this.RelayDifficultyTargetHash, that1.RelayDifficultyTargetHash) { return false } if this.ProofRequestProbability != that1.ProofRequestProbability { @@ -219,10 +221,12 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x15 } - if m.MinRelayDifficultyBits != 0 { - i = encodeVarintParams(dAtA, i, uint64(m.MinRelayDifficultyBits)) + if len(m.RelayDifficultyTargetHash) > 0 { + i -= len(m.RelayDifficultyTargetHash) + copy(dAtA[i:], m.RelayDifficultyTargetHash) + i = encodeVarintParams(dAtA, i, uint64(len(m.RelayDifficultyTargetHash))) i-- - dAtA[i] = 0x8 + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -244,8 +248,9 @@ func (m *Params) Size() (n int) { } var l int _ = l - if m.MinRelayDifficultyBits != 0 { - n += 1 + sovParams(uint64(m.MinRelayDifficultyBits)) + l = len(m.RelayDifficultyTargetHash) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) } if m.ProofRequestProbability != 0 { n += 5 @@ -296,10 +301,10 @@ func (m *Params) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MinRelayDifficultyBits", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RelayDifficultyTargetHash", wireType) } - m.MinRelayDifficultyBits = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowParams @@ -309,11 +314,26 @@ func (m *Params) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.MinRelayDifficultyBits |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } + if byteLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RelayDifficultyTargetHash = append(m.RelayDifficultyTargetHash[:0], dAtA[iNdEx:postIndex]...) + if m.RelayDifficultyTargetHash == nil { + m.RelayDifficultyTargetHash = []byte{} + } + iNdEx = postIndex case 2: if wireType != 5 { return fmt.Errorf("proto: wrong wireType = %d for field ProofRequestProbability", wireType) diff --git a/x/service/types/relay.go b/x/service/types/relay.go index 3276e6441..b43ded9f7 100644 --- a/x/service/types/relay.go +++ b/x/service/types/relay.go @@ -1,48 +1,40 @@ package types import ( - "crypto/sha256" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/pokt-network/poktroll/pkg/crypto/protocol" sessiontypes "github.com/pokt-network/poktroll/x/session/types" sharedtypes "github.com/pokt-network/poktroll/x/shared/types" ) -// GetHashFromBytes returns the hash of the relay (full, request or response) bytes. -// It is used as helper in the case that the relay is already marshaled and -// centralizes the hasher used. -func GetHashFromBytes(relayBz []byte) [32]byte { - return sha256.Sum256(relayBz) -} - // GetHash returns the hash of the relay, which contains both the signed // relay request and the relay response. It is used as the key for insertion // into the SMT. -func (relay *Relay) GetHash() ([32]byte, error) { +func (relay *Relay) GetHash() ([protocol.RelayHasherSize]byte, error) { relayBz, err := relay.Marshal() if err != nil { - return [32]byte{}, err + return [protocol.RelayHasherSize]byte{}, err } - return GetHashFromBytes(relayBz), nil + return protocol.GetRelayHashFromBytes(relayBz), nil } // GetSignableBytesHash returns the hash of the signable bytes of the relay request // Hashing the marshaled request message guarantees that the signable bytes are // always of a constant and expected length. -func (req RelayRequest) GetSignableBytesHash() ([32]byte, error) { +func (req RelayRequest) GetSignableBytesHash() ([protocol.RelayHasherSize]byte, error) { // req and req.Meta are not pointers, so we can set the signature to nil // in order to generate the signable bytes hash without the need restore it. req.Meta.Signature = nil requestBz, err := req.Marshal() if err != nil { - return [32]byte{}, err + return [protocol.RelayHasherSize]byte{}, err } // return the marshaled request hash to guarantee that the signable bytes // are always of a constant and expected length - return GetHashFromBytes(requestBz), nil + return protocol.GetRelayHashFromBytes(requestBz), nil } // ValidateBasic performs basic validation of the RelayResponse Meta, SessionHeader @@ -73,18 +65,18 @@ func (req *RelayRequest) ValidateBasic() error { // GetSignableBytesHash returns the hash of the signable bytes of the relay response // Hashing the marshaled response message guarantees that the signable bytes are // always of a constant and expected length. -func (res RelayResponse) GetSignableBytesHash() ([32]byte, error) { +func (res RelayResponse) GetSignableBytesHash() ([protocol.RelayHasherSize]byte, error) { // res and res.Meta are not pointers, so we can set the signature to nil // in order to generate the signable bytes hash without the need restore it. res.Meta.SupplierSignature = nil responseBz, err := res.Marshal() if err != nil { - return [32]byte{}, err + return [protocol.RelayHasherSize]byte{}, err } // return the marshaled response hash to guarantee that the signable bytes // are always of a constant and expected length - return GetHashFromBytes(responseBz), nil + return protocol.GetRelayHashFromBytes(responseBz), nil } // ValidateBasic performs basic validation of the RelayResponse Meta, SessionHeader diff --git a/x/tokenomics/keeper/msg_server_update_param_test.go b/x/tokenomics/keeper/msg_server_update_param_test.go index f2237bfc1..5f3375e16 100644 --- a/x/tokenomics/keeper/msg_server_update_param_test.go +++ b/x/tokenomics/keeper/msg_server_update_param_test.go @@ -11,6 +11,7 @@ import ( tokenomicstypes "github.com/pokt-network/poktroll/x/tokenomics/types" ) +// TODO_FOLLOWUP(@olshansk, #690): Rename this test. func TestMsgUpdateParam_UpdateMinRelayDifficultyBitsOnly(t *testing.T) { var expectedComputeUnitsToTokensMultiplier int64 = 8 diff --git a/x/tokenomics/keeper/scale_difficulty_test.go b/x/tokenomics/keeper/scale_difficulty_test.go new file mode 100644 index 000000000..7b2cb4cf6 --- /dev/null +++ b/x/tokenomics/keeper/scale_difficulty_test.go @@ -0,0 +1,102 @@ +package keeper + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestScaleDifficultyTargetHash tests the scaling of a target hash by a given ratio. +// Some expectations are manually adjusted to account for some precision loss in the +// implementation. +// TODO_FOLLOWUP(@olshansk, #690): Ensure that the ratio corresponds to the probability of of a relay being accepted. If not, explain why. +func TestScaleDifficultyTargetHash(t *testing.T) { + tests := []struct { + desc string + targetHashHex string + ratio float64 + expectedHashHex string + }{ + { + desc: "Scale by 0.5", + targetHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 0.5, + expectedHashHex: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + { + desc: "Scale by 2", + targetHashHex: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 2, + expectedHashHex: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, + { + desc: "Scale by 0.25", + targetHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 0.25, + expectedHashHex: "3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + { + desc: "Scale by 4", + targetHashHex: "3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 4, + expectedHashHex: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", + }, + { + desc: "Scale by 1 (no change)", + targetHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 1, + expectedHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + { + desc: "Scale by 0.1", + targetHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 0.1, + expectedHashHex: "19999999999999ffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + { + desc: "Scale by 10", + targetHashHex: "1999999999999999999999999999999999999999999999999999999999999999", + ratio: 10, + expectedHashHex: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8", + }, + { + desc: "Scale by 10e-12", + targetHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 10e-12, + expectedHashHex: "000000000afebff0bcb24a7fffffffffffffffffffffffffffffffffffffffff", + }, + { + desc: "Scale by 10e12", + targetHashHex: "000000000afebff0bcb24a7fffffffffffffffffffffffffffffffffffffffff", + ratio: 10e12, + expectedHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + { + desc: "Maxes out at BaseRelayDifficulty", + targetHashHex: "3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ratio: 10, + expectedHashHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + targetHashBz, targetErr := hex.DecodeString(test.targetHashHex) + require.NoError(t, targetErr) + + expectedHashBz, expectedErr := hex.DecodeString(test.expectedHashHex) + require.NoError(t, expectedErr) + + scaledDifficultyHash := scaleDifficultyTargetHash(targetHashBz, new(big.Float).SetFloat64(test.ratio)) + assert.Equal(t, len(scaledDifficultyHash), len(targetHashBz)) + + // Ensure the scaled difficulty hash equals the one provided + require.Zero(t, bytes.Compare(expectedHashBz, scaledDifficultyHash), + "expected difficulty hash %x, but got %x", expectedHashBz, scaledDifficultyHash) + }) + } +} diff --git a/x/tokenomics/keeper/settle_pending_claims.go b/x/tokenomics/keeper/settle_pending_claims.go index 0ad62634c..7fe1a3c53 100644 --- a/x/tokenomics/keeper/settle_pending_claims.go +++ b/x/tokenomics/keeper/settle_pending_claims.go @@ -104,6 +104,7 @@ func (k Keeper) SettlePendingClaims(ctx sdk.Context) ( expiredResult.NumComputeUnits += numClaimComputeUnits continue } + // TODO_FOLLOWUP(@olshansk, #690): Document the potential changes needed here. // NB: If a proof is found, it is valid because verification is done // at the time of submission. } diff --git a/x/tokenomics/keeper/update_relay_mining_difficulty.go b/x/tokenomics/keeper/update_relay_mining_difficulty.go index 340ac77e3..b222b4f37 100644 --- a/x/tokenomics/keeper/update_relay_mining_difficulty.go +++ b/x/tokenomics/keeper/update_relay_mining_difficulty.go @@ -5,8 +5,7 @@ import ( "context" "encoding/hex" "fmt" - "math" - "math/bits" + "math/big" sdk "github.com/cosmos/cosmos-sdk/types" @@ -15,22 +14,24 @@ import ( "github.com/pokt-network/poktroll/x/tokenomics/types" ) -const ( - // Exponential moving average (ema) smoothing factor, commonly known as alpha. - // Usually, alpha = 2 / (N+1), where N is the number of periods. - // Large alpha -> more weight on recent data; less smoothing and fast response. - // Small alpha -> more weight on past data; more smoothing and slow response. - emaSmoothingFactor = float64(0.1) - - // The target number of relays we want the network to mine for a specific - // service across all applications & suppliers per session. - // This number determines the total number of leafs to be created across in - // the off-chain SMTs, across all suppliers, for each service. - // It indirectly drives the off-chain resource requirements of the network - // in additional to playing a critical role in Relay Mining. - // TODO_BLOCKER(@Olshansk, #542): Make this a governance parameter. - TargetNumRelays = uint64(10e4) -) +// TargetNumRelays is the target number of relays we want the network to mine for +// a specific service across all applications & suppliers per session. +// This number determines the total number of leafs to be created across in +// the off-chain SMTs, across all suppliers, for each service. +// It indirectly drives the off-chain resource requirements of the network +// in additional to playing a critical role in Relay Mining. +// TODO_BLOCKER(@Olshansk, #542): Make this a governance parameter. +const TargetNumRelays = uint64(10e4) + +// Exponential moving average (ema) smoothing factor, commonly known as alpha. +// Usually, alpha = 2 / (N+1), where N is the number of periods. +// Large alpha -> more weight on recent data; less smoothing and fast response. +// Small alpha -> more weight on past data; more smoothing and slow response. +// +// TODO_MAINNET: Use a language agnostic float implementation or arithmetic library +// to ensure deterministic results across different language implementations of the +// protocol. +var emaSmoothingFactor = new(big.Float).SetFloat64(0.1) // UpdateRelayMiningDifficulty updates the on-chain relay mining difficulty // based on the amount of on-chain relays for each service, given a map of serviceId->numRelays. @@ -56,7 +57,7 @@ func (k Keeper) UpdateRelayMiningDifficulty( ServiceId: serviceId, BlockHeight: sdkCtx.BlockHeight(), NumRelaysEma: numRelays, - TargetHash: defaultDifficultyTargetHash(), + TargetHash: prooftypes.DefaultRelayDifficultyTargetHash, } } @@ -70,7 +71,7 @@ func (k Keeper) UpdateRelayMiningDifficulty( // Compute the updated EMA of the number of relays. prevRelaysEma := prevDifficulty.NumRelaysEma newRelaysEma := computeEma(alpha, prevRelaysEma, numRelays) - difficultyHash := ComputeNewDifficultyTargetHash(TargetNumRelays, newRelaysEma) + difficultyHash := ComputeNewDifficultyTargetHash(prevDifficulty.TargetHash, TargetNumRelays, newRelaysEma) newDifficulty := types.RelayMiningDifficulty{ ServiceId: serviceId, BlockHeight: sdkCtx.BlockHeight(), @@ -118,91 +119,73 @@ func (k Keeper) UpdateRelayMiningDifficulty( // on the target number of relays we want the network to mine and the new EMA of // the number of relays. // NB: Exported for testing purposes only. -func ComputeNewDifficultyTargetHash(targetNumRelays, newRelaysEma uint64) []byte { +func ComputeNewDifficultyTargetHash(prevTargetHash []byte, targetNumRelays, newRelaysEma uint64) []byte { // The target number of relays we want the network to mine is greater than // the actual on-chain relays, so we don't need to scale to anything above // the default. if targetNumRelays > newRelaysEma { - return defaultDifficultyTargetHash() - } - - log2 := func(x float64) float64 { - return math.Log(x) / math.Ln2 + return prooftypes.DefaultRelayDifficultyTargetHash } - // We are dealing with a bitwise binary distribution, and are trying to convert - // the proportion of an off-chain relay (i.e. relayEMA) to an - // on-chain relay (i.e. target) based on the probability of x leading zeros - // in the target hash. - // - // In other words, the probability of an off-chain relay moving into the tree - // should equal (approximately) the probability of having x leading zeroes - // in the target hash. - // - // The construction is as follows: - // (0.5)^num_leading_zeroes = (num_target_relay / num_total_relays) - // (0.5)^x = (T/R) - // x = -ln2(T/R) - numLeadingZeroBits := int(-log2(float64(targetNumRelays) / float64(newRelaysEma))) - numBytes := protocol.SmtSpec.PathHasherSize() - return LeadingZeroBitsToTargetDifficultyHash(numLeadingZeroBits, numBytes) -} - -// defaultDifficultyTargetHash returns the default difficulty target hash with -// the default number of leading zero bits. -func defaultDifficultyTargetHash() []byte { - numBytes := protocol.SmtSpec.PathHasherSize() - numDefaultLeadingZeroBits := int(prooftypes.DefaultMinRelayDifficultyBits) - return LeadingZeroBitsToTargetDifficultyHash(numDefaultLeadingZeroBits, numBytes) -} - -// computeEma computes the EMA at time t, given the EMA at time t-1, the raw -// data revealed at time t, and the smoothing factor α. -// Src: https://en.wikipedia.org/wiki/Exponential_smoothing -func computeEma(alpha float64, prevEma, currValue uint64) uint64 { - return uint64(alpha*float64(currValue) + (1-alpha)*float64(prevEma)) -} + // Calculate the proportion of target relays relative to the EMA of actual volume applicable relays + // TODO_MAINNET: Use a language agnostic float implementation or arithmetic library + // to ensure deterministic results across different language implementations of the + // protocol. + ratio := new(big.Float).Quo( + new(big.Float).SetUint64(targetNumRelays), + new(big.Float).SetUint64(newRelaysEma), + ) -// RelayMiningTargetHashToDifficulty returns the relay mining difficulty based on the hash. -// This currently implies the number of leading zero bits but may be changed in the future. -// TODO_MAINNET: Determine if we should launch with a more adaptive difficulty or stick -// to leading zeroes. -func RelayMiningTargetHashToDifficulty(targetHash []byte) int { - numLeadingZeroBits := 0 - for _, b := range targetHash { - if b == 0 { - numLeadingZeroBits += 8 - continue - } else { - numLeadingZeroBits += bits.LeadingZeros8(b) - break // Stop counting after the first non-zero byte - } - } + // Compute the new target hash by scaling the previous target hash based on the ratio + newTargetHash := scaleDifficultyTargetHash(prevTargetHash, ratio) - return numLeadingZeroBits + return newTargetHash } -// LeadingZeroBitsToTargetDifficultyHash generates a slice of bytes with the specified number of leading zero bits -// NB: Exported for testing purposes only. -func LeadingZeroBitsToTargetDifficultyHash(numLeadingZeroBits int, numBytes int) []byte { - targetDifficultyHah := make([]byte, numBytes) - - // Set everything to 1s initially - for i := range targetDifficultyHah { - targetDifficultyHah[i] = 0xff +// scaleDifficultyTargetHash scales the target hash based on the given ratio +// +// TODO_MAINNET: Use a language agnostic float implementation or arithmetic library +// to ensure deterministic results across different language implementations of the +// protocol. +func scaleDifficultyTargetHash(targetHash []byte, ratio *big.Float) []byte { + // Convert targetHash to a big.Float to miminize precision loss. + targetInt := new(big.Int).SetBytes(targetHash) + targetFloat := new(big.Float).SetInt(targetInt) + + // Scale the target by multiplying it by the ratio. + scaledTargetFloat := new(big.Float).Mul(targetFloat, ratio) + // NB: Some precision is lost when converting back to an integer. + scaledTargetInt, _ := scaledTargetFloat.Int(nil) + scaledTargetHash := scaledTargetInt.Bytes() + + // Ensure the scaled target hash maxes out at BaseRelayDifficulty + if len(scaledTargetHash) > len(targetHash) { + return protocol.BaseRelayDifficultyHashBz } - // Set full zero bytes - fullZeroBytes := numLeadingZeroBits / 8 - for i := 0; i < fullZeroBytes; i++ { - targetDifficultyHah[i] = 0 + // Ensure the scaled target hash has the same length as the default target hash. + if len(scaledTargetHash) < len(targetHash) { + paddedTargetHash := make([]byte, len(targetHash)) + copy(paddedTargetHash[len(paddedTargetHash)-len(scaledTargetHash):], scaledTargetHash) + return paddedTargetHash } - // Set remaining bits in the next byte - remainingZeroBits := numLeadingZeroBits % 8 - if remainingZeroBits > 0 { - targetDifficultyHah[fullZeroBytes] = byte(0xff >> remainingZeroBits) - } + return scaledTargetHash +} - return targetDifficultyHah +// computeEma computes the EMA at time t, given the EMA at time t-1, the raw +// data revealed at time t, and the smoothing factor α. +// Src: https://en.wikipedia.org/wiki/Exponential_smoothing +// +// TODO_MAINNET: Use a language agnostic float implementation or arithmetic library +// to ensure deterministic results across different language implementations of the +// protocol. +func computeEma(alpha *big.Float, prevEma, currValue uint64) uint64 { + oneMinusAlpha := new(big.Float).Sub(new(big.Float).SetInt64(1), alpha) + prevEmaFloat := new(big.Float).SetUint64(prevEma) + + weightedCurrentContribution := new(big.Float).Mul(alpha, new(big.Float).SetUint64(currValue)) + weightedPreviousContribution := new(big.Float).Mul(oneMinusAlpha, prevEmaFloat) + newEma, _ := new(big.Float).Add(weightedCurrentContribution, weightedPreviousContribution).Uint64() + return newEma } diff --git a/x/tokenomics/keeper/update_relay_mining_difficulty_test.go b/x/tokenomics/keeper/update_relay_mining_difficulty_test.go index c9cde4b13..fef8272ec 100644 --- a/x/tokenomics/keeper/update_relay_mining_difficulty_test.go +++ b/x/tokenomics/keeper/update_relay_mining_difficulty_test.go @@ -9,6 +9,7 @@ import ( testutilevents "github.com/pokt-network/poktroll/testutil/events" keepertest "github.com/pokt-network/poktroll/testutil/keeper" + prooftypes "github.com/pokt-network/poktroll/x/proof/types" "github.com/pokt-network/poktroll/x/tokenomics/keeper" tokenomicskeeper "github.com/pokt-network/poktroll/x/tokenomics/keeper" "github.com/pokt-network/poktroll/x/tokenomics/types" @@ -127,8 +128,8 @@ func TestUpdateRelayMiningDifficulty_FirstDifficulty(t *testing.T) { BlockHeight: 1, NumRelaysEma: keeper.TargetNumRelays * 1e3, TargetHash: append( - []byte{0b00000000, 0b01111111}, // 9 leading 0 bits - makeBytesFullOfOnes(30)..., + []byte{0b00000000}, // at least 8 leading 0 bits + makeBytesFullOfOnes(31)..., ), }, }, @@ -142,41 +143,46 @@ func TestUpdateRelayMiningDifficulty_FirstDifficulty(t *testing.T) { _, err := keeper.UpdateRelayMiningDifficulty(ctx, relaysPerServiceMap) require.NoError(t, err) - difficulty, found := keeper.GetRelayMiningDifficulty(ctx, "svc1") + relayDifficulty, found := keeper.GetRelayMiningDifficulty(ctx, "svc1") require.True(t, found) - require.Equal(t, difficulty.NumRelaysEma, tt.numRelays) - require.Equal(t, difficulty.NumRelaysEma, tt.expectedRelayMiningDifficulty.NumRelaysEma) + require.Equal(t, tt.numRelays, relayDifficulty.NumRelaysEma) + require.Equal(t, tt.expectedRelayMiningDifficulty.NumRelaysEma, relayDifficulty.NumRelaysEma) - require.Equal(t, difficulty.TargetHash, tt.expectedRelayMiningDifficulty.TargetHash) + // NB: An increase in difficulty is indicated by a decrease in the target hash + didDifficultyIncrease := bytes.Compare(relayDifficulty.TargetHash, tt.expectedRelayMiningDifficulty.TargetHash) < 1 + require.True(t, didDifficultyIncrease, + "expected difficulty.TargetHash (%x) to be less than or equal to expectedRelayMiningDifficulty.TargetHash (%x)", + relayDifficulty.TargetHash, tt.expectedRelayMiningDifficulty.TargetHash, + ) }) } } func TestComputeNewDifficultyHash(t *testing.T) { tests := []struct { - desc string - numRelaysTarget uint64 - relaysEma uint64 - expectedDifficultyHash []byte + desc string + numRelaysTarget uint64 + relaysEma uint64 + expectedRelayDifficultyHash []byte }{ { - desc: "Relays Target > Relays EMA", - numRelaysTarget: 100, - relaysEma: 50, - expectedDifficultyHash: defaultDifficulty(), + desc: "Relays Target > Relays EMA", + numRelaysTarget: 100, + relaysEma: 50, + expectedRelayDifficultyHash: defaultDifficulty(), }, { - desc: "Relays Target == Relays EMA", - numRelaysTarget: 100, - relaysEma: 100, - expectedDifficultyHash: defaultDifficulty(), + desc: "Relays Target == Relays EMA", + numRelaysTarget: 100, + relaysEma: 100, + expectedRelayDifficultyHash: defaultDifficulty(), }, { desc: "Relays Target < Relays EMA", numRelaysTarget: 50, relaysEma: 100, - expectedDifficultyHash: append( + expectedRelayDifficultyHash: append( []byte{0b01111111}, makeBytesFullOfOnes(31)..., ), @@ -185,7 +191,7 @@ func TestComputeNewDifficultyHash(t *testing.T) { desc: "Relays Target << Relays EMA", numRelaysTarget: 50, relaysEma: 200, - expectedDifficultyHash: append( + expectedRelayDifficultyHash: append( []byte{0b00111111}, makeBytesFullOfOnes(31)..., ), @@ -194,7 +200,7 @@ func TestComputeNewDifficultyHash(t *testing.T) { desc: "Relays Target << Relays EMA", numRelaysTarget: 50, relaysEma: 1000, - expectedDifficultyHash: append( + expectedRelayDifficultyHash: append( []byte{0b00001111}, makeBytesFullOfOnes(31)..., ), @@ -203,7 +209,7 @@ func TestComputeNewDifficultyHash(t *testing.T) { desc: "Relays Target << Relays EMA", numRelaysTarget: 50, relaysEma: 10000, - expectedDifficultyHash: append( + expectedRelayDifficultyHash: append( []byte{0b00000001}, makeBytesFullOfOnes(31)..., ), @@ -212,88 +218,24 @@ func TestComputeNewDifficultyHash(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - result := keeper.ComputeNewDifficultyTargetHash(tt.numRelaysTarget, tt.relaysEma) - require.Equal(t, result, tt.expectedDifficultyHash) - }) - } -} - -func TestLeadingZeroBitsToTargetDifficultyHash(t *testing.T) { - tests := []struct { - desc string - numLeadingZeroBits int - numBytes int - expectedDifficultyHash []byte - }{ - { - desc: "0 leading 0 bits in 1 byte", - numLeadingZeroBits: 0, - numBytes: 1, - expectedDifficultyHash: []byte{0b11111111}, - }, - { - desc: "full zero bytes (16 leading 0 bits in 32 bytes)", - numLeadingZeroBits: 16, - numBytes: 32, - expectedDifficultyHash: append( - []byte{0b00000000, 0b00000000}, - makeBytesFullOfOnes(30)..., - ), - }, - { - desc: "partial byte (20 leading 0 bits in 32 bytes)", - numLeadingZeroBits: 20, - numBytes: 32, - expectedDifficultyHash: append( - []byte{0b00000000, 0b00000000, 0b00001111}, - makeBytesFullOfOnes(29)..., - ), - }, - { - desc: "another partial byte (10 leading 0 bits in 32 bytes)", - numLeadingZeroBits: 10, - numBytes: 32, - expectedDifficultyHash: append( - []byte{0b00000000, 0b00111111}, - makeBytesFullOfOnes(30)..., - ), - }, - { - desc: "edge case 1 bit (1 leading 0 bits in 32 bytes)", - numLeadingZeroBits: 1, - numBytes: 32, - expectedDifficultyHash: append( - []byte{0b01111111}, - makeBytesFullOfOnes(31)..., - ), - }, - { - desc: "exact byte boundary (24 leading 0 bits in 32 bytes)", - numLeadingZeroBits: 24, - numBytes: 32, - expectedDifficultyHash: append( - []byte{0b00000000, 0b00000000, 0b00000000}, - makeBytesFullOfOnes(29)..., - ), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - result := keeper.LeadingZeroBitsToTargetDifficultyHash(tt.numLeadingZeroBits, tt.numBytes) - if !bytes.Equal(result, tt.expectedDifficultyHash) { - t.Errorf("got %x, expected %x", result, tt.expectedDifficultyHash) - } + newRelayDifficultyTargetHash := keeper.ComputeNewDifficultyTargetHash(prooftypes.DefaultRelayDifficultyTargetHash, tt.numRelaysTarget, tt.relaysEma) + + // NB: An increase in difficulty is indicated by a decrease in the target hash + didDifficultyIncrease := bytes.Compare(newRelayDifficultyTargetHash, tt.expectedRelayDifficultyHash) < 1 + require.True(t, didDifficultyIncrease, + "expected difficulty.TargetHash (%x) to be less than or equal to expectedRelayMiningDifficulty.TargetHash (%x)", + newRelayDifficultyTargetHash, tt.expectedRelayDifficultyHash, + ) }) } } func makeBytesFullOfOnes(length int) []byte { - result := make([]byte, length) - for i := range result { - result[i] = 0b11111111 + output := make([]byte, length) + for i := range output { + output[i] = 0b11111111 } - return result + return output } func defaultDifficulty() []byte { diff --git a/x/tokenomics/module/abci.go b/x/tokenomics/module/abci.go index b628bc553..1f40f2895 100644 --- a/x/tokenomics/module/abci.go +++ b/x/tokenomics/module/abci.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/pokt-network/poktroll/pkg/crypto/protocol" "github.com/pokt-network/poktroll/telemetry" prooftypes "github.com/pokt-network/poktroll/x/proof/types" "github.com/pokt-network/poktroll/x/tokenomics/keeper" @@ -13,6 +14,7 @@ import ( // EndBlocker called at every block and settles all pending claims. func EndBlocker(ctx sdk.Context, k keeper.Keeper) (err error) { logger := k.Logger().With("method", "EndBlocker") + // NB: There are two main reasons why we settle expiring claims in the end // instead of when a proof is submitted: // 1. Logic - Probabilistic proof allows claims to be settled (i.e. rewarded) @@ -25,7 +27,13 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (err error) { return err } - // Defer telemetry calls so that they reference the final values the relevant variables. + logger.Info(fmt.Sprintf( + "settled %d claims and expired %d claims", + settledResult.NumClaims, + expiredResult.NumClaims, + )) + + // Telemetry - defer telemetry calls so that they reference the final values the relevant variables. defer func() { telemetry.ClaimCounter( prooftypes.ClaimProofStage_SETTLED, @@ -61,12 +69,6 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (err error) { // TODO_IMPROVE(#observability): Add a counter for expired compute units. }() - logger.Info(fmt.Sprintf( - "settled %d claims and expired %d claims", - settledResult.NumClaims, - expiredResult.NumClaims, - )) - // Update the relay mining difficulty for every service that settled pending // claims based on how many estimated relays were serviced for it. difficultyPerServiceMap, err := k.UpdateRelayMiningDifficulty(ctx, settledResult.RelaysPerServiceMap) @@ -79,11 +81,16 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (err error) { len(settledResult.RelaysPerServiceMap), )) - // Emit telemetry for each service's relay mining difficulty. - for serviceId, newDifficulty := range difficultyPerServiceMap { - miningDifficultyNumBits := keeper.RelayMiningTargetHashToDifficulty(newDifficulty.TargetHash) - telemetry.RelayMiningDifficultyGauge(miningDifficultyNumBits, serviceId) - telemetry.RelayEMAGauge(newDifficulty.NumRelaysEma, serviceId) + // Telemetry - emit telemetry for each service's relay mining difficulty. + for serviceId, newRelayMiningDifficulty := range difficultyPerServiceMap { + var newRelayMiningTargetHash [protocol.RelayHasherSize]byte + copy(newRelayMiningTargetHash[:], newRelayMiningDifficulty.TargetHash) + + // NB: The difficulty integer is just a human readable interpretation of + // the target hash and is not actually used for business logic. + difficulty := protocol.GetDifficultyFromHash(newRelayMiningTargetHash) + telemetry.RelayMiningDifficultyGauge(difficulty, serviceId) + telemetry.RelayEMAGauge(newRelayMiningDifficulty.NumRelaysEma, serviceId) } return nil