mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* [NOD-1079] Fix block rejects over "Already have block" (#783) * [NOD-1079] Return regular error instead of ruleError on already-have-block in ProcessBlock. * [NOD-1079] Fix bad implementation of IsSelectedTipKnown. * [NOD-1079] In shouldQueryPeerSelectedTips use selected DAG tip timestamp instead of past median time. * [NOD-1079] Remove redundant (and possibly buggy) clearing of sm.requestedBlocks. * [NOD-684] change simnet block rate to block per ms (#782) * [NOD-684] Get rid of dag.targetTimePerBlock and use finality duration in dag params * [NOD-684] Fix regtest genesis block * [NOD-684] Set simnet's TargetTimePerBlock to 1ms * [NOD-684] Shorten simnet finality duration * [NOD-684] Change isDAGCurrentMaxDiff to be written as number of blocks * [NOD-684] Fix NextBlockMinimumTime to be add one millisecond after past median time * [NOD-1004] Make AddrManager.getAddress use only 1 loop to check all address chances and pick one of them (#741) * [NOD-1004] Remove code duplication in Good(). * [NOD-1004] Remove some more code duplication in Good(). * [NOD-1004] Remove some more code duplication in Good(). * [NOD-1004] Remove code duplication in GetAddress(). * [NOD-1004] Remove code duplication in updateAddress. * [NOD-1004] Remove some more code duplication in updateAddress. * [NOD-1004] Remove redundant check in expireNew. * [NOD-1004] Remove superfluous existence check from updateAddress. * [NOD-1004] Make triedBucket use a slice instead of a list. * [NOD-1004] Remove code duplication in getAddress. * [NOD-1004] Remove infinite loops out of getAddress. * [NOD-1004] Made impossible branch panic. * [NOD-1004] Remove a mystery comment. * [NOD-1004] Remove an unnecessary sort. * [NOD-1004] Make AddressKey a type alias. * [NOD-1004] Added comment for AddressKey * [NOD-1004] Fix merge errors. * [NOD-1004] Fix merge errors. * [NOD-1004] Do some renaming. * [NOD-1004] Do some more renaming. * [NOD-1004] Rename AddrManager to AddressManager. * [NOD-1004] Rename AddrManager to AddressManager. * [NOD-1004] Do some more renaming. * [NOD-1004] Rename bucket to addressBucketArray. * [NOD-1004] Fix a comment. * [NOD-1004] Rename na to netAddress. * [NOD-1004] Bring back an existence check. * [NOD-1004] Fix an error message. * [NOD-1004] Fix a comment. * [NOD-1004] Use a boolean instead of -1. * [NOD-1004] Use a boolean instead of -1 in another place. Co-authored-by: Mike Zak <feanorr@gmail.com> * Fix merge errors. * [NOD-1181] Move isBanned logic into addressManager. * [NOD-1181] Persist bans to disk. * [NOD-1181] Add comments. * [NOD-1181] Add an additional exit condition to the connection loop. * [NOD-1181] Add a TODO. * [NOD-1181] Wrap not-found errors in addressManager. * [NOD-1181] Fix a comment. * [NOD-1181] Rename banned to isBanned. * [NOD-1181] Fix bad error handling in routerInitializer. * [NOD-1181] Remove a TODO. Co-authored-by: Ori Newman <orinewman1@gmail.com> Co-authored-by: Mike Zak <feanorr@gmail.com>
476 lines
18 KiB
Go
476 lines
18 KiB
Go
// Copyright (c) 2014-2017 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/kaspanet/kaspad/addressmanager"
|
|
)
|
|
|
|
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
|
|
// the verbose flag is set. When the verbose flag is not set, getblockheader
|
|
// returns a hex-encoded string.
|
|
type GetBlockHeaderVerboseResult struct {
|
|
Hash string `json:"hash"`
|
|
Confirmations uint64 `json:"confirmations"`
|
|
Version int32 `json:"version"`
|
|
VersionHex string `json:"versionHex"`
|
|
HashMerkleRoot string `json:"hashMerkleRoot"`
|
|
AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"`
|
|
Time int64 `json:"time"`
|
|
Nonce uint64 `json:"nonce"`
|
|
Bits string `json:"bits"`
|
|
Difficulty float64 `json:"difficulty"`
|
|
ParentHashes []string `json:"parentHashes,omitempty"`
|
|
SelectedParentHash string `json:"selectedParentHash"`
|
|
ChildHashes []string `json:"childHashes,omitempty"`
|
|
}
|
|
|
|
// GetBlockVerboseResult models the data from the getblock command when the
|
|
// verbose flag is set. When the verbose flag is not set, getblock returns a
|
|
// hex-encoded string.
|
|
type GetBlockVerboseResult struct {
|
|
Hash string `json:"hash"`
|
|
Confirmations uint64 `json:"confirmations"`
|
|
Size int32 `json:"size"`
|
|
BlueScore uint64 `json:"blueScore"`
|
|
IsChainBlock bool `json:"isChainBlock"`
|
|
Version int32 `json:"version"`
|
|
VersionHex string `json:"versionHex"`
|
|
HashMerkleRoot string `json:"hashMerkleRoot"`
|
|
AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"`
|
|
UTXOCommitment string `json:"utxoCommitment"`
|
|
Tx []string `json:"tx,omitempty"`
|
|
RawTx []TxRawResult `json:"rawRx,omitempty"`
|
|
Time int64 `json:"time"`
|
|
Nonce uint64 `json:"nonce"`
|
|
Bits string `json:"bits"`
|
|
Difficulty float64 `json:"difficulty"`
|
|
ParentHashes []string `json:"parentHashes"`
|
|
SelectedParentHash string `json:"selectedParentHash"`
|
|
ChildHashes []string `json:"childHashes"`
|
|
AcceptedBlockHashes []string `json:"acceptedBlockHashes"`
|
|
}
|
|
|
|
// CreateMultiSigResult models the data returned from the createmultisig
|
|
// command.
|
|
type CreateMultiSigResult struct {
|
|
Address string `json:"address"`
|
|
RedeemScript string `json:"redeemScript"`
|
|
}
|
|
|
|
// DecodeScriptResult models the data returned from the decodescript command.
|
|
type DecodeScriptResult struct {
|
|
Asm string `json:"asm"`
|
|
Type string `json:"type"`
|
|
Address *string `json:"address,omitempty"`
|
|
P2sh string `json:"p2sh,omitempty"`
|
|
}
|
|
|
|
// SoftForkDescription describes the current state of a soft-fork which was
|
|
// deployed using a super-majority block signalling.
|
|
type SoftForkDescription struct {
|
|
ID string `json:"id"`
|
|
Version uint32 `json:"version"`
|
|
Reject struct {
|
|
Status bool `json:"status"`
|
|
} `json:"reject"`
|
|
}
|
|
|
|
// Bip9SoftForkDescription describes the current state of a defined BIP0009
|
|
// version bits soft-fork.
|
|
type Bip9SoftForkDescription struct {
|
|
Status string `json:"status"`
|
|
Bit uint8 `json:"bit"`
|
|
StartTime int64 `json:"startTime"`
|
|
Timeout int64 `json:"timeout"`
|
|
Since int32 `json:"since"`
|
|
}
|
|
|
|
// GetBlockDAGInfoResult models the data returned from the getblockdaginfo
|
|
// command.
|
|
type GetBlockDAGInfoResult struct {
|
|
DAG string `json:"dag"`
|
|
Blocks uint64 `json:"blocks"`
|
|
Headers uint64 `json:"headers"`
|
|
TipHashes []string `json:"tipHashes"`
|
|
Difficulty float64 `json:"difficulty"`
|
|
MedianTime int64 `json:"medianTime"`
|
|
VerificationProgress float64 `json:"verificationProgress,omitempty"`
|
|
Pruned bool `json:"pruned"`
|
|
PruneHeight uint64 `json:"pruneHeight,omitempty"`
|
|
DAGWork string `json:"dagWork,omitempty"`
|
|
SoftForks []*SoftForkDescription `json:"softForks"`
|
|
Bip9SoftForks map[string]*Bip9SoftForkDescription `json:"bip9SoftForks"`
|
|
}
|
|
|
|
// GetBlockTemplateResultTx models the transactions field of the
|
|
// getblocktemplate command.
|
|
type GetBlockTemplateResultTx struct {
|
|
Data string `json:"data"`
|
|
ID string `json:"id"`
|
|
Depends []int64 `json:"depends"`
|
|
Mass uint64 `json:"mass"`
|
|
Fee uint64 `json:"fee"`
|
|
}
|
|
|
|
// GetBlockTemplateResult models the data returned from the getblocktemplate
|
|
// command.
|
|
type GetBlockTemplateResult struct {
|
|
// Base fields from BIP 0022. CoinbaseAux is optional. One of
|
|
// CoinbaseTxn or CoinbaseValue must be specified, but not both.
|
|
Bits string `json:"bits"`
|
|
CurTime int64 `json:"curTime"`
|
|
Height uint64 `json:"height"`
|
|
ParentHashes []string `json:"parentHashes"`
|
|
MassLimit int64 `json:"massLimit,omitempty"`
|
|
Transactions []GetBlockTemplateResultTx `json:"transactions"`
|
|
HashMerkleRoot string `json:"hashMerkleRoot"`
|
|
AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"`
|
|
UTXOCommitment string `json:"utxoCommitment"`
|
|
Version int32 `json:"version"`
|
|
WorkID string `json:"workId,omitempty"`
|
|
IsSynced bool `json:"isSynced"`
|
|
|
|
// Optional long polling from BIP 0022.
|
|
LongPollID string `json:"longPollId,omitempty"`
|
|
LongPollURI string `json:"longPollUri,omitempty"`
|
|
|
|
// Basic pool extension from BIP 0023.
|
|
Target string `json:"target,omitempty"`
|
|
Expires int64 `json:"expires,omitempty"`
|
|
|
|
// Mutations from BIP 0023.
|
|
MaxTime int64 `json:"maxTime,omitempty"`
|
|
MinTime int64 `json:"minTime,omitempty"`
|
|
Mutable []string `json:"mutable,omitempty"`
|
|
NonceRange string `json:"nonceRange,omitempty"`
|
|
|
|
// Block proposal from BIP 0023.
|
|
Capabilities []string `json:"capabilities,omitempty"`
|
|
RejectReason string `json:"rejectReason,omitempty"`
|
|
}
|
|
|
|
// GetMempoolEntryResult models the data returned from the getMempoolEntry
|
|
// command.
|
|
type GetMempoolEntryResult struct {
|
|
Fee uint64 `json:"fee"`
|
|
Time int64 `json:"time"`
|
|
RawTx TxRawResult `json:"rawTx"`
|
|
}
|
|
|
|
// GetMempoolInfoResult models the data returned from the getmempoolinfo
|
|
// command.
|
|
type GetMempoolInfoResult struct {
|
|
Size int64 `json:"size"`
|
|
Bytes int64 `json:"bytes"`
|
|
}
|
|
|
|
// NetworksResult models the networks data from the getnetworkinfo command.
|
|
type NetworksResult struct {
|
|
Name string `json:"name"`
|
|
Limited bool `json:"limited"`
|
|
Reachable bool `json:"reachable"`
|
|
Proxy string `json:"proxy"`
|
|
ProxyRandomizeCredentials bool `json:"proxyRandomizeCredentials"`
|
|
}
|
|
|
|
// LocalAddressesResult models the localaddresses data from the getnetworkinfo
|
|
// command.
|
|
type LocalAddressesResult struct {
|
|
Address string `json:"address"`
|
|
Port uint16 `json:"port"`
|
|
Score int32 `json:"score"`
|
|
}
|
|
|
|
// GetNetworkInfoResult models the data returned from the getnetworkinfo
|
|
// command.
|
|
type GetNetworkInfoResult struct {
|
|
Version int32 `json:"version"`
|
|
SubVersion string `json:"subVersion"`
|
|
ProtocolVersion int32 `json:"protocolVersion"`
|
|
LocalServices string `json:"localServices"`
|
|
LocalRelay bool `json:"localRelay"`
|
|
TimeOffset int64 `json:"timeOffset"`
|
|
Connections int32 `json:"connections"`
|
|
NetworkActive bool `json:"networkActive"`
|
|
Networks []NetworksResult `json:"networks"`
|
|
RelayFee float64 `json:"relayFee"`
|
|
IncrementalFee float64 `json:"incrementalFee"`
|
|
LocalAddresses []LocalAddressesResult `json:"localAddresses"`
|
|
Warnings string `json:"warnings"`
|
|
}
|
|
|
|
// GetConnectedPeerInfoResult models the data returned from the getConnectedPeerInfo command.
|
|
type GetConnectedPeerInfoResult struct {
|
|
ID string `json:"id"`
|
|
Address string `json:"address"`
|
|
LastPingDuration int64 `json:"lastPingDuration"`
|
|
SelectedTipHash string `json:"selectedTipHash"`
|
|
IsSyncNode bool `json:"isSyncNode"`
|
|
IsOutbound bool `json:"isOutbound"`
|
|
TimeOffset int64 `json:"timeOffset"`
|
|
UserAgent string `json:"userAgent"`
|
|
AdvertisedProtocolVersion uint32 `json:"advertisedProtocolVersion"`
|
|
TimeConnected int64 `json:"timeConnected"`
|
|
}
|
|
|
|
// GetPeerAddressesResult models the data returned from the getPeerAddresses command.
|
|
type GetPeerAddressesResult struct {
|
|
Version int
|
|
Key [32]byte
|
|
Addresses []*GetPeerAddressesKnownAddressResult
|
|
NewBuckets map[string]*GetPeerAddressesNewBucketResult // string is Subnetwork ID
|
|
NewBucketFullNodes GetPeerAddressesNewBucketResult
|
|
TriedBuckets map[string]*GetPeerAddressesTriedBucketResult // string is Subnetwork ID
|
|
TriedBucketFullNodes GetPeerAddressesTriedBucketResult
|
|
}
|
|
|
|
// GetPeerAddressesKnownAddressResult models a GetPeerAddressesResult known address.
|
|
type GetPeerAddressesKnownAddressResult struct {
|
|
Addr string
|
|
Src string
|
|
SubnetworkID string
|
|
Attempts int
|
|
TimeStamp int64
|
|
LastAttempt int64
|
|
LastSuccess int64
|
|
IsBanned bool
|
|
BannedTime int64
|
|
}
|
|
|
|
// GetPeerAddressesNewBucketResult models a GetPeerAddressesResult new bucket.
|
|
type GetPeerAddressesNewBucketResult [addressmanager.NewBucketCount][]string
|
|
|
|
// GetPeerAddressesTriedBucketResult models a GetPeerAddressesResult tried bucket.
|
|
type GetPeerAddressesTriedBucketResult [addressmanager.TriedBucketCount][]string
|
|
|
|
// GetRawMempoolVerboseResult models the data returned from the getrawmempool
|
|
// command when the verbose flag is set. When the verbose flag is not set,
|
|
// getrawmempool returns an array of transaction hashes.
|
|
type GetRawMempoolVerboseResult struct {
|
|
Size int32 `json:"size"`
|
|
Fee float64 `json:"fee"`
|
|
Time int64 `json:"time"`
|
|
Depends []string `json:"depends"`
|
|
}
|
|
|
|
// ScriptPubKeyResult models the scriptPubKey data of a tx script. It is
|
|
// defined separately since it is used by multiple commands.
|
|
type ScriptPubKeyResult struct {
|
|
Asm string `json:"asm"`
|
|
Hex string `json:"hex,omitempty"`
|
|
Type string `json:"type"`
|
|
Address *string `json:"address,omitempty"`
|
|
}
|
|
|
|
// GetSubnetworkResult models the data from the getSubnetwork command.
|
|
type GetSubnetworkResult struct {
|
|
GasLimit *uint64 `json:"gasLimit"`
|
|
}
|
|
|
|
// GetTxOutResult models the data from the gettxout command.
|
|
type GetTxOutResult struct {
|
|
SelectedTip string `json:"selectedTip"`
|
|
Confirmations *uint64 `json:"confirmations,omitempty"`
|
|
IsInMempool bool `json:"isInMempool"`
|
|
Value float64 `json:"value"`
|
|
ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
|
|
Coinbase bool `json:"coinbase"`
|
|
}
|
|
|
|
// GetNetTotalsResult models the data returned from the getnettotals command.
|
|
type GetNetTotalsResult struct {
|
|
TotalBytesRecv uint64 `json:"totalBytesRecv"`
|
|
TotalBytesSent uint64 `json:"totalBytesSent"`
|
|
TimeMillis int64 `json:"timeMillis"`
|
|
}
|
|
|
|
// ScriptSig models a signature script. It is defined separately since it only
|
|
// applies to non-coinbase. Therefore the field in the Vin structure needs
|
|
// to be a pointer.
|
|
type ScriptSig struct {
|
|
Asm string `json:"asm"`
|
|
Hex string `json:"hex"`
|
|
}
|
|
|
|
// Vin models parts of the tx data.
|
|
type Vin struct {
|
|
TxID string `json:"txId"`
|
|
Vout uint32 `json:"vout"`
|
|
ScriptSig *ScriptSig `json:"scriptSig"`
|
|
Sequence uint64 `json:"sequence"`
|
|
}
|
|
|
|
// MarshalJSON provides a custom Marshal method for Vin.
|
|
func (v *Vin) MarshalJSON() ([]byte, error) {
|
|
txStruct := struct {
|
|
TxID string `json:"txId"`
|
|
Vout uint32 `json:"vout"`
|
|
ScriptSig *ScriptSig `json:"scriptSig"`
|
|
Sequence uint64 `json:"sequence"`
|
|
}{
|
|
TxID: v.TxID,
|
|
Vout: v.Vout,
|
|
ScriptSig: v.ScriptSig,
|
|
Sequence: v.Sequence,
|
|
}
|
|
return json.Marshal(txStruct)
|
|
}
|
|
|
|
// PrevOut represents previous output for an input Vin.
|
|
type PrevOut struct {
|
|
Address *string `json:"address,omitempty"`
|
|
Value float64 `json:"value"`
|
|
}
|
|
|
|
// VinPrevOut is like Vin except it includes PrevOut.
|
|
type VinPrevOut struct {
|
|
Coinbase string `json:"coinbase"`
|
|
TxID string `json:"txId"`
|
|
Vout uint32 `json:"vout"`
|
|
ScriptSig *ScriptSig `json:"scriptSig"`
|
|
PrevOut *PrevOut `json:"prevOut"`
|
|
Sequence uint64 `json:"sequence"`
|
|
}
|
|
|
|
// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not.
|
|
func (v *VinPrevOut) IsCoinBase() bool {
|
|
return len(v.Coinbase) > 0
|
|
}
|
|
|
|
// MarshalJSON provides a custom Marshal method for VinPrevOut.
|
|
func (v *VinPrevOut) MarshalJSON() ([]byte, error) {
|
|
if v.IsCoinBase() {
|
|
coinbaseStruct := struct {
|
|
Coinbase string `json:"coinbase"`
|
|
Sequence uint64 `json:"sequence"`
|
|
}{
|
|
Coinbase: v.Coinbase,
|
|
Sequence: v.Sequence,
|
|
}
|
|
return json.Marshal(coinbaseStruct)
|
|
}
|
|
|
|
txStruct := struct {
|
|
TxID string `json:"txId"`
|
|
Vout uint32 `json:"vout"`
|
|
ScriptSig *ScriptSig `json:"scriptSig"`
|
|
PrevOut *PrevOut `json:"prevOut,omitempty"`
|
|
Sequence uint64 `json:"sequence"`
|
|
}{
|
|
TxID: v.TxID,
|
|
Vout: v.Vout,
|
|
ScriptSig: v.ScriptSig,
|
|
PrevOut: v.PrevOut,
|
|
Sequence: v.Sequence,
|
|
}
|
|
return json.Marshal(txStruct)
|
|
}
|
|
|
|
// Vout models parts of the tx data
|
|
type Vout struct {
|
|
Value uint64 `json:"value"`
|
|
N uint32 `json:"n"`
|
|
ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
|
|
}
|
|
|
|
// GetWorkResult models the data from the getwork command.
|
|
type GetWorkResult struct {
|
|
Data string `json:"data"`
|
|
Hash1 string `json:"hash1"`
|
|
Midstate string `json:"midstate"`
|
|
Target string `json:"target"`
|
|
}
|
|
|
|
// InfoDAGResult models the data returned by the kaspa rpc server getinfo command.
|
|
type InfoDAGResult struct {
|
|
Version string `json:"version"`
|
|
ProtocolVersion int32 `json:"protocolVersion"`
|
|
Blocks uint64 `json:"blocks"`
|
|
Connections int32 `json:"connections"`
|
|
Proxy string `json:"proxy"`
|
|
Difficulty float64 `json:"difficulty"`
|
|
Testnet bool `json:"testnet"`
|
|
Devnet bool `json:"devnet"`
|
|
RelayFee float64 `json:"relayFee"`
|
|
Errors string `json:"errors"`
|
|
}
|
|
|
|
// TxRawResult models transaction result data.
|
|
type TxRawResult struct {
|
|
Hex string `json:"hex"`
|
|
TxID string `json:"txId"`
|
|
Hash string `json:"hash,omitempty"`
|
|
Size int32 `json:"size,omitempty"`
|
|
Version int32 `json:"version"`
|
|
LockTime uint64 `json:"lockTime"`
|
|
Subnetwork string `json:"subnetwork"`
|
|
Gas uint64 `json:"gas"`
|
|
PayloadHash string `json:"payloadHash"`
|
|
Payload string `json:"payload"`
|
|
Vin []Vin `json:"vin"`
|
|
Vout []Vout `json:"vout"`
|
|
BlockHash string `json:"blockHash,omitempty"`
|
|
AcceptedBy *string `json:"acceptedBy,omitempty"`
|
|
IsInMempool bool `json:"isInMempool"`
|
|
Time uint64 `json:"time,omitempty"`
|
|
BlockTime uint64 `json:"blockTime,omitempty"`
|
|
}
|
|
|
|
// TxRawDecodeResult models the data from the decoderawtransaction command.
|
|
type TxRawDecodeResult struct {
|
|
TxID string `json:"txId"`
|
|
Version int32 `json:"version"`
|
|
Locktime uint64 `json:"lockTime"`
|
|
Vin []Vin `json:"vin"`
|
|
Vout []Vout `json:"vout"`
|
|
}
|
|
|
|
// ValidateAddressResult models the data returned by the kaspa rpc server
|
|
// validateaddress command.
|
|
type ValidateAddressResult struct {
|
|
IsValid bool `json:"isValid"`
|
|
Address string `json:"address,omitempty"`
|
|
}
|
|
|
|
// ChainBlock models a block that is part of the selected parent chain.
|
|
type ChainBlock struct {
|
|
Hash string `json:"hash"`
|
|
AcceptedBlocks []AcceptedBlock `json:"acceptedBlocks"`
|
|
}
|
|
|
|
// AcceptedBlock models a block that is included in the blues of a selected
|
|
// chain block.
|
|
type AcceptedBlock struct {
|
|
Hash string `json:"hash"`
|
|
AcceptedTxIDs []string `json:"acceptedTxIds"`
|
|
}
|
|
|
|
// GetChainFromBlockResult models the data from the getChainFromBlock command.
|
|
type GetChainFromBlockResult struct {
|
|
RemovedChainBlockHashes []string `json:"removedChainBlockHashes"`
|
|
AddedChainBlocks []ChainBlock `json:"addedChainBlocks"`
|
|
Blocks []GetBlockVerboseResult `json:"blocks"`
|
|
}
|
|
|
|
// GetBlocksResult models the data from the getBlocks command.
|
|
type GetBlocksResult struct {
|
|
Hashes []string `json:"hashes"`
|
|
RawBlocks []string `json:"rawBlocks"`
|
|
VerboseBlocks []GetBlockVerboseResult `json:"verboseBlocks"`
|
|
}
|
|
|
|
// VersionResult models objects included in the version response. In the actual
|
|
// result, these objects are keyed by the program or API name.
|
|
type VersionResult struct {
|
|
VersionString string `json:"versionString"`
|
|
Major uint32 `json:"major"`
|
|
Minor uint32 `json:"minor"`
|
|
Patch uint32 `json:"patch"`
|
|
Prerelease string `json:"prerelease"`
|
|
BuildMetadata string `json:"buildMetadata"`
|
|
}
|