feat(consensus): enforce hf5 tx version and add asset descriptors
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
f7ee451fc4
commit
d3143d3f88
4 changed files with 123 additions and 3 deletions
|
|
@ -75,14 +75,16 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
|
|||
// checkTxVersion validates that the transaction version is appropriate for the
|
||||
// current hardfork era.
|
||||
//
|
||||
// After HF5: transaction version must be >= VersionPostHF5 (3).
|
||||
// After HF5: transaction version must be exactly VersionPostHF5 (3) and the
|
||||
// embedded hardfork_id must match the active hardfork version.
|
||||
// Before HF5: transaction version 3 is rejected (too early).
|
||||
func checkTxVersion(tx *types.Transaction, forks []config.HardFork, height uint64) error {
|
||||
hf5Active := config.IsHardForkActive(forks, config.HF5, height)
|
||||
currentFork := config.VersionAtHeight(forks, height)
|
||||
|
||||
if hf5Active && tx.Version < types.VersionPostHF5 {
|
||||
if hf5Active && tx.Version != types.VersionPostHF5 {
|
||||
return coreerr.E("checkTxVersion",
|
||||
fmt.Sprintf("version %d too low after HF5 at height %d", tx.Version, height),
|
||||
fmt.Sprintf("version %d invalid after HF5 at height %d", tx.Version, height),
|
||||
ErrTxVersionInvalid)
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +94,12 @@ func checkTxVersion(tx *types.Transaction, forks []config.HardFork, height uint6
|
|||
ErrTxVersionInvalid)
|
||||
}
|
||||
|
||||
if hf5Active && tx.HardforkID != currentFork {
|
||||
return coreerr.E("checkTxVersion",
|
||||
fmt.Sprintf("hardfork id %d does not match active fork %d at height %d", tx.HardforkID, currentFork, height),
|
||||
ErrTxVersionInvalid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,20 @@ func TestCheckTxVersion_Bad(t *testing.T) {
|
|||
}{
|
||||
// v2 transaction after HF5 — must be v3.
|
||||
{"v2_after_hf5", validV2Tx(), config.TestnetForks, 250},
|
||||
// v3 transaction after HF5 with wrong hardfork id.
|
||||
{"v3_after_hf5_wrong_hardfork", func() *types.Transaction {
|
||||
tx := validV3Tx()
|
||||
tx.HardforkID = 4
|
||||
return tx
|
||||
}(), config.TestnetForks, 250},
|
||||
// v3 transaction before HF5 — too early.
|
||||
{"v3_before_hf5", validV3Tx(), config.TestnetForks, 150},
|
||||
// future version must be rejected.
|
||||
{"v4_after_hf5", func() *types.Transaction {
|
||||
tx := validV3Tx()
|
||||
tx.Version = types.VersionPostHF5 + 1
|
||||
return tx
|
||||
}(), config.TestnetForks, 250},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
40
types/asset.go
Normal file
40
types/asset.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
||||
//
|
||||
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package types
|
||||
|
||||
// Asset operation types used by the HF5 asset_descriptor_operation variant.
|
||||
const (
|
||||
AssetOpRegister uint8 = 0 // deploy new asset
|
||||
AssetOpEmit uint8 = 1 // emit additional supply
|
||||
AssetOpUpdate uint8 = 2 // update asset metadata
|
||||
AssetOpBurn uint8 = 3 // burn supply with proof
|
||||
AssetOpPublicBurn uint8 = 4 // burn supply publicly
|
||||
)
|
||||
|
||||
// AssetDescriptorBase holds the core asset metadata referenced by
|
||||
// asset_descriptor_operation extra variants.
|
||||
type AssetDescriptorBase struct {
|
||||
Ticker string
|
||||
FullName string
|
||||
TotalMaxSupply uint64
|
||||
CurrentSupply uint64
|
||||
DecimalPoint uint8
|
||||
MetaInfo string
|
||||
OwnerKey PublicKey
|
||||
Etc []byte
|
||||
}
|
||||
|
||||
// AssetDescriptorOperation represents a deploy/emit/update/burn operation.
|
||||
// The wire format is parsed in wire/ as an opaque blob for round-tripping.
|
||||
type AssetDescriptorOperation struct {
|
||||
Version uint8
|
||||
OperationType uint8
|
||||
Descriptor *AssetDescriptorBase
|
||||
AssetID Hash
|
||||
AmountToEmit uint64
|
||||
AmountToBurn uint64
|
||||
Etc []byte
|
||||
}
|
||||
60
types/asset_test.go
Normal file
60
types/asset_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
||||
//
|
||||
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAssetOperationConstants_Good(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
got uint8
|
||||
want uint8
|
||||
}{
|
||||
{"register", AssetOpRegister, 0},
|
||||
{"emit", AssetOpEmit, 1},
|
||||
{"update", AssetOpUpdate, 2},
|
||||
{"burn", AssetOpBurn, 3},
|
||||
{"public_burn", AssetOpPublicBurn, 4},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.got != tt.want {
|
||||
t.Fatalf("got %d, want %d", tt.got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssetDescriptorTypes_Good(t *testing.T) {
|
||||
base := AssetDescriptorBase{
|
||||
Ticker: "LTHN",
|
||||
FullName: "Lethean",
|
||||
TotalMaxSupply: 1000000,
|
||||
CurrentSupply: 0,
|
||||
DecimalPoint: 12,
|
||||
MetaInfo: "{}",
|
||||
OwnerKey: PublicKey{1},
|
||||
Etc: []byte{1, 2, 3},
|
||||
}
|
||||
|
||||
op := AssetDescriptorOperation{
|
||||
Version: 1,
|
||||
OperationType: AssetOpRegister,
|
||||
Descriptor: &base,
|
||||
AssetID: Hash{1},
|
||||
AmountToEmit: 100,
|
||||
AmountToBurn: 10,
|
||||
Etc: []byte{4, 5, 6},
|
||||
}
|
||||
|
||||
if op.Descriptor == nil || op.Descriptor.Ticker != "LTHN" {
|
||||
t.Fatalf("unexpected descriptor: %+v", op.Descriptor)
|
||||
}
|
||||
if op.OperationType != AssetOpRegister || op.Version != 1 {
|
||||
t.Fatalf("unexpected operation: %+v", op)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue