mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +00:00
qkc: port QuarkChain helper packages (account, common, params, config) (#16)
* qkc/{common,account,params,config}: port QuarkChain helper packages
- account: Branch (fullShardId codec), Address, Identity, keystore Account
(pborman/uuid -> google/uuid); python-generated golden testdata, sampled
to 1600 Branch vectors (all 16 shard sizes); golden keystore files
proving on-disk load compatibility across the uuid swap.
- common (+hexutil): IsP2/IntLeftMostBit/token-id codec and the QuarkChain
hexutil fork (lenient leading zeros) the config JSON depends on, with
goquarkchain's modified tests locking in the lenient behaviour.
- params: QKC EVM constants and DefaultConstantinople.
- config: QuarkChainConfig/ChainConfig/ShardConfig/RootConfig (passive),
JSON-identical; verified against goquarkchain's
cluster_config_template.json (its derivative of the python testnet
template).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* qkc/params: remove unused MainnetBootnodes (master's concern, not the slave's)
bootnodes.go only defined an empty, unused MainnetBootnodes var (no references
anywhere in the tree). In QuarkChain's master/slave architecture, public-network
P2P peer discovery is handled by the master; a slave connects to its master and
has no use for a bootnode list, so it does not belong in the shard params.
Addresses review feedback from @qzhodl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/params: set goshard version to 1.0.0
The ported version.go carried goquarkchain's 1.9.0; goshard should have its own
version number, which we'll bump by phase going forward. Start it at 1.0.0.
Addresses review feedback from @qzhodl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/params: set PetersburgBlock in DefaultConstantinople
goquarkchain doesn't set PetersburgBlock, but its forked gasSStore hard-codes
the legacy (pre-EIP-1283) SSTORE metering via an `if true` (the EIP-1283 net-
metering path is left as dead code) — so its EVM already runs Petersburg
behavior. Stock geth gates that revert on PetersburgBlock, so set it to 0
alongside Constantinople to reproduce goquarkchain's EVM faithfully.
No effect until execution lands (deferred); this fixes the target fork config.
Addresses review feedback from @qzhodl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/params: drop non-versioning constants from version.go
NEW_TRANSACTION_LIST_LIMIT and TPS_Num are tx-pool/throughput constants that
goquarkchain happened to keep in version.go; they're unused here and unrelated
to versioning. Remove them so version.go holds only version info. They'll be
reintroduced where they belong when the tx-pool/execution work lands.
Addresses review feedback from @qzhodl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/account: remove empty keystore/ placeholder folder
qkc/account/keystore/ held only a .gitignore (*.json) — an empty folder carried
over verbatim from goquarkchain's layout. Nothing references it, and runtime
keystores live under the datadir, not the source tree. Removing the .gitignore
drops the empty dir.
Addresses review feedback from @qzhodl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/config: match pyquarkchain's WEBSOCKET_JSON_RPC_PORT (single optional port)
slave_config carried goquarkchain's WEBSOCKET_JSON_RPC_PORT_LIST ([]uint16, with
a per-shard DefaultWSPort+offset derivation), but the goshard slave reads a
pyquarkchain master's cluster config, where SlaveConfig has a single optional
WEBSOCKET_JSON_RPC_PORT (default None; the WS server only starts when it's set).
Switch to WSPort *uint16 (nil = None) and drop the goquarkchain per-shard
derivation plus the now-unused DefaultWSPort.
Addresses review feedback from @qzhodl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/config: drop goquarkchain-only GRPCHost/GRPCPort
gRPC is goquarkchain's master<->slave transport and has no pyquarkchain
counterpart (cluster_config.py has no gRPC), so it isn't needed for faithful
pyquarkchain config compatibility. Both fields were json:"-" (invisible to the
JSON round-trip / byte-compat test) and were never read anywhere under qkc/
(set in constructors only). Remove GRPCHost/GRPCPort from RootConfig and
QuarkChainConfig, their constructor assignments, and the DefaultGrpcPort const.
common.GetIPV4Addr is kept as a general util (likely needed for the slave's own
HOST auto-detection later).
Addresses review feedback from @qzhodl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/config: round-trip DEFAULT_CHAIN_TOKEN (was json:"-")
ChainConfig.DefaultChainToken was tagged json:"-" (faithfully ported from
goquarkchain), so DEFAULT_CHAIN_TOKEN was silently dropped on marshal/unmarshal.
pyquarkchain's ChainConfig has DEFAULT_CHAIN_TOKEN ("QKC") as a real config
field, so losing it isn't pyquarkchain-faithful. Tag it DEFAULT_CHAIN_TOKEN so
it round-trips (the custom Marshal/UnmarshalJSON embed ChainConfigAlias, which
inherits the tag).
Addresses review feedback from @syntrust.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/config: reject missing CHAIN_ID=0 in chain-id validation
initAndValidate checked chain ids with i != chainIDMap[i], but chainIDMap is
built chainID->chainID, so a missing key returns the zero value 0. For i=0 that
collides with the expected value (0 != 0 is false), so a config missing
CHAIN_ID=0 silently passed validation. Check key presence instead
(_, ok := chainIDMap[i]; !ok), which correctly rejects any missing chain id,
including 0.
Addresses review feedback from @syntrust.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/config: drop inverted dead comparisons in TestLoadClusterConfig
The per-chain Genesis/PoswConfig/ConsensusConfig comparisons used
if reflect.DeepEqual(go, py) { Fatalf } -- inverted (missing !), so they only
fire when the values are equal and otherwise validate nothing. The bug exists
upstream too (goquarkchain cluster/config/config_test.go:142-150) and was
carried over by the verbatim port.
They cannot be fixed by adding !: the two fixtures (test_config.json and the
python cluster_config_template.json) deliberately differ on those value fields
(difficulty, block time, extra data, stake), so an equality assertion would
always fail. The test's meaningful purpose is to verify the Go and Python
configs describe the same cluster topology, so drop the three broken checks and
keep the slave-list / chain-count / per-chain CHAIN_ID+SHARD_SIZE assertions.
Addresses review feedback from @syntrust.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/config: derive per-shard coinbase and GENESIS.ALLOC like pyquarkchain
QuarkChainConfig.UnmarshalJSON copied the chain coinbase and the full
GENESIS.ALLOC to every shard verbatim (goquarkchain's behavior). pyquarkchain's
ClusterConfig.from_dict instead derives each shard config by rewriting the
coinbase into the shard via address_in_shard(full_shard_id), and filtering
GENESIS.ALLOC to the addresses whose shard bits match the shard
(full_shard_key & (SHARD_SIZE-1) == shard_id).
The goshard slave shares its config with a pyquarkchain master, so the per-shard
config must be derived exactly as pyquarkchain derives it.
TestShardConfigDerivation verifies both against test_config.json: every shard's
coinbase full-shard-key equals its full shard id, and chain 2's three
allocations (all with shard bits 0) land on shard 0 with shard 1 filtered empty.
Addresses review feedback from @syntrust.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* qkc/config: fix RootSignerPrivateKey json:"_" typo that leaked the key
RootSignerPrivateKey ([]byte) was tagged json:"_", which Go treats as a literal
field name "_", not "ignore" (only json:"-" ignores). The field is excluded
from the alias and re-emitted as hex by the jsonConfig wrapper
(ROOT_SIGNER_PRIVATE_KEY), like the sibling GuardianPublicKey which correctly uses
json:"-". As written, MarshalJSON emitted both ROOT_SIGNER_PRIVATE_KEY and a
spurious "_":"<base64>" key exposing the raw private-key bytes. Use json:"-"
to match GuardianPublicKey and pyquarkchain (which emits only
ROOT_SIGNER_PRIVATE_KEY). goquarkchain has the same typo at
cluster/config/cluster_config.go:149.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0a76f2a7fc
commit
4160ab81ee
37 changed files with 6671 additions and 0 deletions
246
qkc/account/account.go
Normal file
246
qkc/account/account.go
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
// Ported from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
// Adaptation: github.com/pborman/uuid -> github.com/google/uuid (the only uuid
|
||||
// package available in this module); identical wire/JSON behaviour.
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
// Account include Identity address and ID
|
||||
type Account struct {
|
||||
Identity Identity
|
||||
QKCAddress Address
|
||||
ID uuid.UUID
|
||||
}
|
||||
|
||||
// EncryptedKeyJSON keystore file included
|
||||
type EncryptedKeyJSON struct {
|
||||
Address string `json:"address"`
|
||||
Crypto CryptoJSON `json:"crypto"`
|
||||
ID string `json:"id"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
// CryptoJSON crypto data for keystore file
|
||||
type CryptoJSON struct {
|
||||
Cipher string `json:"cipher"`
|
||||
CipherText string `json:"ciphertext"`
|
||||
CipherParams cipherParamsJSON `json:"cipherparams"`
|
||||
KDF string `json:"kdf"`
|
||||
KDFParams map[string]interface{} `json:"kdfparams"`
|
||||
MAC string `json:"mac"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
type cipherParamsJSON struct {
|
||||
IV string `json:"iv"`
|
||||
}
|
||||
|
||||
func newAccount(identity Identity, address Address) Account {
|
||||
return Account{
|
||||
ID: uuid.New(),
|
||||
Identity: identity,
|
||||
QKCAddress: address,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAccountWithKey create new account with key
|
||||
func NewAccountWithKey(key Key) (Account, error) {
|
||||
identity, err := CreatIdentityFromKey(key)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
defaultFullShardKey, err := identity.GetDefaultFullShardKey()
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
address := CreatAddressFromIdentity(identity, defaultFullShardKey)
|
||||
return newAccount(identity, address), nil
|
||||
}
|
||||
|
||||
// NewAccountWithoutKey new account without key,use random key
|
||||
func NewAccountWithoutKey() (Account, error) {
|
||||
identity, err := CreatRandomIdentity()
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
defaultFullShardKey, err := identity.GetDefaultFullShardKey()
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
address := CreatAddressFromIdentity(identity, defaultFullShardKey)
|
||||
return newAccount(identity, address), nil
|
||||
}
|
||||
|
||||
// Load load a keystore file with password
|
||||
func Load(path string, password string) (Account, error) {
|
||||
jsonData, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
var keystoreJSONData EncryptedKeyJSON
|
||||
err = json.Unmarshal(jsonData, &keystoreJSONData)
|
||||
key, err := DecodeKeyStoreJSON(keystoreJSONData, password)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
keyTypeData := BytesToIdentityKey(key)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
account, err := NewAccountWithKey(keyTypeData)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
if keystoreJSONData.ID != "" {
|
||||
account.ID, _ = uuid.Parse(keystoreJSONData.ID)
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// DecodeKeyStoreJSON decode key with password ,return plainText to create account
|
||||
func DecodeKeyStoreJSON(keystoreJSONData EncryptedKeyJSON, password string) ([]byte, error) {
|
||||
kdfParams := keystoreJSONData.Crypto.KDFParams
|
||||
c := ensureInt(kdfParams[kdfParamsC])
|
||||
salt, err := hex.DecodeString(kdfParams[kdfParamsSalt].(string))
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
dkLen := ensureInt(kdfParams[kdfParamsPrfDkLen])
|
||||
derivedKey := pbkdf2.Key([]byte(password), salt, c, dkLen, sha256.New)
|
||||
if len(derivedKey) < 32 { // derived key must be at least 32 bytes long
|
||||
return []byte{}, errors.New("derivedkey<32")
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(keystoreJSONData.Crypto.CipherParams.IV)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(keystoreJSONData.Crypto.CipherText)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
mac := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
macJSON, err := hex.DecodeString(keystoreJSONData.Crypto.MAC)
|
||||
if err != nil {
|
||||
return []byte{}, errors.New("decode Mac failed")
|
||||
}
|
||||
if !bytes.Equal(mac, macJSON) {
|
||||
return []byte{}, errors.New("mac is not match")
|
||||
}
|
||||
|
||||
plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return plainText, nil
|
||||
}
|
||||
|
||||
// Dump dump a keystore file with it's password
|
||||
func (Self *Account) Dump(password string, includeAddress bool, write bool, directory string) ([]byte, error) {
|
||||
keystoreJSON, err := Self.MakeKeyStoreJSON(password)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
if includeAddress {
|
||||
address := Self.Address()
|
||||
keystoreJSON.Address = address
|
||||
}
|
||||
|
||||
data, err := json.Marshal(keystoreJSON)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
if write {
|
||||
if directory == "" {
|
||||
directory = DefaultKeyStoreDirectory
|
||||
}
|
||||
|
||||
filepath := directory + Self.ID.String() + ".json"
|
||||
err := writeKeyFile(filepath, data)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// MakeKeyStoreJSON make encrypt Json depend on it's password
|
||||
func (Self *Account) MakeKeyStoreJSON(password string) (EncryptedKeyJSON, error) {
|
||||
salt := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||
return EncryptedKeyJSON{}, errors.New("get salt failed")
|
||||
}
|
||||
|
||||
kdfParams := make(map[string]interface{}, 5)
|
||||
kdfParams[kdfParamsPrf] = kdfParamsPrfValue
|
||||
kdfParams[kdfParamsPrfDkLen] = kdfParamsPrfDkLenValue
|
||||
kdfParams[kdfParamsC] = kdfParamsCValue
|
||||
kdfParams[kdfParamsSalt] = hex.EncodeToString(salt)
|
||||
derivedKey := pbkdf2.Key([]byte(password), salt, kdfParamsCValue, kdfParamsPrfDkLenValue, sha256.New)
|
||||
encKey := derivedKey[:16]
|
||||
cipherParams := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, cipherParams); err != nil {
|
||||
return EncryptedKeyJSON{}, errors.New("get cipherparams failed")
|
||||
}
|
||||
|
||||
cipherText, err := aesCTRXOR(encKey, Self.Identity.key.Bytes(), cipherParams)
|
||||
if err != nil {
|
||||
return EncryptedKeyJSON{}, errors.New("aes error")
|
||||
}
|
||||
|
||||
mac := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
cryptoData := CryptoJSON{
|
||||
Cipher: cryptoCipher,
|
||||
CipherText: hex.EncodeToString(cipherText),
|
||||
CipherParams: cipherParamsJSON{
|
||||
IV: hex.EncodeToString(cipherParams),
|
||||
},
|
||||
KDF: cryptoKDF,
|
||||
KDFParams: kdfParams,
|
||||
MAC: hex.EncodeToString(mac),
|
||||
Version: cryptoVersion,
|
||||
}
|
||||
return EncryptedKeyJSON{
|
||||
ID: Self.ID.String(),
|
||||
Crypto: cryptoData,
|
||||
Version: jsonVersion,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Address return it's real address
|
||||
func (Self *Account) Address() string {
|
||||
return Self.QKCAddress.ToHex()
|
||||
}
|
||||
|
||||
// PrivateKey return it's key
|
||||
func (Self *Account) PrivateKey() string {
|
||||
return hex.EncodeToString(Self.Identity.key.Bytes())
|
||||
}
|
||||
|
||||
// UUID return it's uuid
|
||||
func (Self *Account) UUID() uuid.UUID {
|
||||
return Self.ID
|
||||
}
|
||||
105
qkc/account/account_test.go
Normal file
105
qkc/account/account_test.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type AccountTestStruct struct {
|
||||
TKey string `json:"tKey"`
|
||||
Address string `json:"address"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
|
||||
// get abs file path
|
||||
func GetFilesAndDirs(dirPth string) (files []string, err error) {
|
||||
dir, err := ioutil.ReadDir(dirPth)
|
||||
if err != nil { //read dir err
|
||||
return nil, err
|
||||
}
|
||||
PthSep := string(os.PathSeparator)
|
||||
for _, fi := range dir {
|
||||
if fi.IsDir() {
|
||||
continue
|
||||
} else {
|
||||
ok := strings.HasSuffix(fi.Name(), ".json")
|
||||
if ok {
|
||||
files = append(files, dirPth+PthSep+fi.Name()) // is keystore file
|
||||
}
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func CheckAccountUnitTest(data AccountTestStruct, pathAll []string) bool {
|
||||
accountPath := ""
|
||||
for _, v := range pathAll { //find keystore file depend on uuid
|
||||
if strings.Contains(v, data.UUID) {
|
||||
accountPath = v
|
||||
}
|
||||
}
|
||||
if accountPath == "" { //can not find file
|
||||
fmt.Println("can not find path")
|
||||
return false
|
||||
}
|
||||
|
||||
account, err := Load(accountPath, data.TKey)
|
||||
if err != nil { //load err
|
||||
fmt.Println("Load err", err)
|
||||
return false
|
||||
}
|
||||
address := account.Address()
|
||||
if "0x"+data.Address != address { //address is not match
|
||||
fmt.Printf("address is not match unexcepted %s,excepted %s\n", data.Address, address)
|
||||
return false
|
||||
}
|
||||
if data.UUID != account.UUID().String() { //uuid is not match
|
||||
fmt.Println("uuid is not match")
|
||||
return false
|
||||
}
|
||||
if data.PrivateKey != account.PrivateKey() { //privateKey is not match
|
||||
fmt.Println("privateKey is not match")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 1.python generate keystore and it's value
|
||||
// 2.go.exe test it
|
||||
func TestAccount(t *testing.T) {
|
||||
files, err := GetFilesAndDirs("./testdata/keystore/") //read test keystore file
|
||||
JSONParse := NewJSONStruct()
|
||||
data := []AccountTestStruct{}
|
||||
err = JSONParse.Load("./testdata/testAccount.json", &data) //analysis test data
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
count := 0
|
||||
for _, v := range data {
|
||||
err := CheckAccountUnitTest(v, files) //unit test
|
||||
if err == false {
|
||||
panic(-1)
|
||||
}
|
||||
count++
|
||||
}
|
||||
fmt.Println("TestAccount:success test num:", count)
|
||||
}
|
||||
|
||||
// 1.dump file
|
||||
// 2.use python to load and check it's value
|
||||
func TestDump(t *testing.T) {
|
||||
account, err := NewAccountWithoutKey()
|
||||
fmt.Println("err", err)
|
||||
fmt.Println("id", account.ID.String())
|
||||
fmt.Println("Private", account.PrivateKey())
|
||||
fmt.Println("Address", account.Address())
|
||||
_, err = account.Dump("test_password", true, true, "")
|
||||
fmt.Println("dump err", err)
|
||||
}
|
||||
185
qkc/account/address.go
Normal file
185
qkc/account/address.go
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/qkc/common"
|
||||
)
|
||||
|
||||
// Address include recipient and fullShardKey
|
||||
type Address struct {
|
||||
Recipient Recipient
|
||||
FullShardKey uint32
|
||||
}
|
||||
|
||||
// NewAddress new address with recipient and fullShardKey
|
||||
func NewAddress(recipient Recipient, fullShardKey uint32) Address {
|
||||
return Address{
|
||||
Recipient: recipient,
|
||||
FullShardKey: fullShardKey,
|
||||
}
|
||||
}
|
||||
|
||||
// ToHex return bytes included recipient and fullShardKey
|
||||
func (Self Address) ToHex() string {
|
||||
address := Self.ToBytes()
|
||||
return hexutil.Encode(address)
|
||||
|
||||
}
|
||||
|
||||
func (Self Address) ToBytes() []byte {
|
||||
address := Self.Recipient.Bytes()
|
||||
shardKey := Uint32ToBytes(Self.FullShardKey)
|
||||
address = append(address, shardKey...)
|
||||
return address
|
||||
}
|
||||
|
||||
// GetFullShardID get fullShardID depend shardSize
|
||||
func (Self *Address) GetFullShardID(shardSize uint32) (uint32, error) {
|
||||
if common.IsP2(shardSize) == false {
|
||||
return 0, fmt.Errorf("shardSize is not right shardSize:%d", shardSize)
|
||||
}
|
||||
|
||||
chainID := Self.FullShardKey >> 16
|
||||
shardID := Self.FullShardKey & (shardSize - 1)
|
||||
return uint32(chainID<<16 | shardSize | shardID), nil
|
||||
}
|
||||
|
||||
func (self *Address) GetChainID() uint32 {
|
||||
return self.FullShardKey >> 16
|
||||
}
|
||||
|
||||
// AddressInShard return address depend new fullShardKey
|
||||
func (Self *Address) AddressInShard(fullShardKey uint32) Address {
|
||||
return NewAddress(Self.Recipient, fullShardKey)
|
||||
}
|
||||
|
||||
// AddressInBranch return address depend new branch
|
||||
func (Self *Address) AddressInBranch(branch Branch) Address {
|
||||
shardKey := Self.FullShardKey & ((1 << 16) - 1)
|
||||
newShardKey := (shardKey & ^(branch.GetShardSize() - 1)) + branch.GetShardID()
|
||||
newFullShardKey := branch.GetChainID()<<16 | newShardKey
|
||||
return NewAddress(Self.Recipient, newFullShardKey)
|
||||
}
|
||||
|
||||
// CreatAddressFromIdentity creat address from identity
|
||||
func CreatAddressFromIdentity(identity Identity, fullShardKey uint32) Address {
|
||||
return NewAddress(identity.recipient, fullShardKey)
|
||||
}
|
||||
|
||||
// CreatRandomAccountWithFullShardKey creat random account with fullShardKey
|
||||
func CreatRandomAccountWithFullShardKey(fullShardKey uint32) (Address, error) {
|
||||
identity, err := CreatRandomIdentity()
|
||||
if err != nil {
|
||||
return Address{}, err
|
||||
}
|
||||
return CreatAddressFromIdentity(identity, fullShardKey), nil
|
||||
}
|
||||
|
||||
// CreatRandomAccountWithoutFullShardKey creat random account without fullShardKey
|
||||
func CreatRandomAccountWithoutFullShardKey() (Address, error) {
|
||||
identity, err := CreatRandomIdentity()
|
||||
if err != nil {
|
||||
return Address{}, err
|
||||
}
|
||||
|
||||
defaultFullShardKey, err := identity.GetDefaultFullShardKey()
|
||||
if err != nil {
|
||||
return Address{}, err
|
||||
}
|
||||
return CreatAddressFromIdentity(identity, defaultFullShardKey), nil
|
||||
}
|
||||
|
||||
// CreatEmptyAddress creat empty address from fullShardKey
|
||||
func CreatEmptyAddress(fullShardKey uint32) Address {
|
||||
zeroBytes := make([]byte, RecipientLength)
|
||||
recipient := BytesToIdentityRecipient(zeroBytes)
|
||||
return NewAddress(recipient, fullShardKey)
|
||||
}
|
||||
|
||||
// CreatAddressFromBytes creat address from bytes
|
||||
func CreatAddressFromBytes(bs []byte) (Address, error) {
|
||||
if len(bs) != RecipientLength+FullShardKeyLength {
|
||||
return Address{}, fmt.Errorf("bs length excepted %d,unexcepted %d", RecipientLength+FullShardKeyLength, len(bs))
|
||||
}
|
||||
|
||||
buffer := bytes.NewBuffer(bs[RecipientLength:])
|
||||
var x uint32
|
||||
err := binary.Read(buffer, binary.BigEndian, &x)
|
||||
if err != nil {
|
||||
return Address{}, err
|
||||
}
|
||||
recipient := BytesToIdentityRecipient(bs[0:RecipientLength])
|
||||
return NewAddress(recipient, x), nil
|
||||
}
|
||||
|
||||
// IsEmpty check address is empty
|
||||
func (Self *Address) IsEmpty() bool {
|
||||
zero := make([]byte, RecipientLength)
|
||||
return bytes.Equal(zero, Self.Recipient.Bytes())
|
||||
}
|
||||
|
||||
var (
|
||||
addressT = reflect.TypeOf(Address{})
|
||||
)
|
||||
|
||||
// MarshalJSON Address serialisation
|
||||
func (Self Address) MarshalJSON() (out []byte, err error) {
|
||||
return []byte(`"` + Self.ToHex() + `"`), nil
|
||||
}
|
||||
|
||||
func (Self *Address) UnmarshalJSON(data []byte) error {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
input := strings.TrimSpace(string(data))
|
||||
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
|
||||
input = input[1 : len(input)-1]
|
||||
} else {
|
||||
return errors.New("address unmarshal failed ,should with \" \"")
|
||||
}
|
||||
|
||||
if !common.Has0xPrefix(input) {
|
||||
return errors.New("should have 0x prefix")
|
||||
}
|
||||
|
||||
input = input[2:]
|
||||
if len(input) != 0 && len(input) != 48 {
|
||||
return errors.New("failed: len should 0 or 48")
|
||||
}
|
||||
if len(input) == 0 {
|
||||
return nil
|
||||
}
|
||||
*Self, err = CreatAddressFromBytes(ethCommon.FromHex(input))
|
||||
return err
|
||||
}
|
||||
|
||||
type UnprefixedAddress Address
|
||||
|
||||
func (Self UnprefixedAddress) Address() Address {
|
||||
return Address{
|
||||
Recipient: Self.Recipient,
|
||||
FullShardKey: Self.FullShardKey,
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText Address serialisation
|
||||
func (Self UnprefixedAddress) MarshalText() (out []byte, err error) {
|
||||
return []byte(Self.Address().ToHex()), nil
|
||||
}
|
||||
|
||||
func (Self *UnprefixedAddress) UnmarshalText(dataWithout0x []byte) error {
|
||||
addr, err := CreatAddressFromBytes(ethCommon.FromHex(string(dataWithout0x)))
|
||||
Self.Recipient = addr.Recipient
|
||||
Self.FullShardKey = addr.FullShardKey
|
||||
return err
|
||||
}
|
||||
151
qkc/account/address_test.go
Normal file
151
qkc/account/address_test.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type AddressTestStruct struct {
|
||||
Type string `json:"type"`
|
||||
IsEmpty bool `json:"isEmpty"`
|
||||
ToHex string `json:"toHex"`
|
||||
FullSizeData uint32 `json:"T_FullSize_Data"`
|
||||
FullShardID uint32 `json:"T_FullShardId"`
|
||||
BranchData uint32 `json:"T_Branch_Data"`
|
||||
BranchToHex string `json:"T_Branch_toHex"`
|
||||
TShard uint32 `json:"T_Shard"`
|
||||
ShardToHex string `json:"T_shard_toHex"`
|
||||
TKey string `json:"tKey"`
|
||||
FullShardKey uint32 `json:"t_full_shard_key"`
|
||||
}
|
||||
|
||||
func CheckAddressUnitTest(data AddressTestStruct) bool {
|
||||
tAddress := Address{}
|
||||
switch data.Type {
|
||||
case "empty":
|
||||
tAddress = CreatEmptyAddress(data.FullShardKey) //test creatEmptyAccount
|
||||
case "bs":
|
||||
bs, err := hex.DecodeString(data.TKey)
|
||||
if err != nil {
|
||||
fmt.Println("decodeString bs failed:err", err)
|
||||
return false
|
||||
}
|
||||
tAddress, err = CreatAddressFromBytes(bs) //create address from bs
|
||||
if err != nil {
|
||||
fmt.Println("create address from bs failed err", err)
|
||||
return false
|
||||
}
|
||||
case "identity":
|
||||
tkey, err := hex.DecodeString(data.TKey) //create address from special key
|
||||
if err != nil {
|
||||
fmt.Println("decodeString tKey failed err", err)
|
||||
return false
|
||||
}
|
||||
keyType := BytesToIdentityKey(tkey)
|
||||
tIdentity, err := CreatIdentityFromKey(keyType)
|
||||
tAddress = CreatAddressFromIdentity(tIdentity, data.FullShardKey)
|
||||
}
|
||||
|
||||
if tAddress.IsEmpty() != data.IsEmpty { //checkIsEmpty
|
||||
fmt.Println("tAddress.IsEmpty is not match")
|
||||
return false
|
||||
}
|
||||
toHex := tAddress.ToHex()
|
||||
if toHex != "0x"+data.ToHex { //checkToHex
|
||||
fmt.Println("toHex is not match")
|
||||
return false
|
||||
}
|
||||
fullShardID, err := tAddress.GetFullShardID(data.FullSizeData) //checkFullSizeData
|
||||
if err != nil {
|
||||
fmt.Println("GetFullShardKey err", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if fullShardID != data.FullShardID {
|
||||
fmt.Println("fullShardId is not match")
|
||||
return false
|
||||
}
|
||||
|
||||
tBranch := Branch{
|
||||
Value: data.BranchData,
|
||||
}
|
||||
addressInBranch := tAddress.AddressInBranch(tBranch) //check address's toHex depend addressInBranch
|
||||
toHex = addressInBranch.ToHex()
|
||||
if toHex != "0x"+data.BranchToHex {
|
||||
fmt.Println("addressInBranch.Tohex is not match ")
|
||||
return false
|
||||
}
|
||||
|
||||
addressInShard := tAddress.AddressInShard(data.TShard) //checkShardIDInBranch
|
||||
toHex = addressInShard.ToHex()
|
||||
if toHex != "0x"+data.ShardToHex {
|
||||
fmt.Printf("addressInShard is not match : unexcepted %s,excepted %s\n", hex.EncodeToString([]byte(toHex)), data.ShardToHex)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// 1.python generate testdata
|
||||
//
|
||||
// 1.1 empty address
|
||||
// 1.2 address from bytes
|
||||
// 1.3 address from special key
|
||||
//
|
||||
// 2.go.exe to check
|
||||
//
|
||||
// 2.1 checkIsEmpty
|
||||
// 2.2 checkToHex
|
||||
// 2.3 checkFullShardID
|
||||
// 2.4 checkAddressInBranch
|
||||
// 2.5 checkShardIDInBranch
|
||||
func TestAddress(t *testing.T) {
|
||||
JSONParse := NewJSONStruct()
|
||||
data := []AddressTestStruct{}
|
||||
err := JSONParse.Load("./testdata/testAddress.json", &data) //analysis test data
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
count := 0
|
||||
for _, v := range data {
|
||||
if err := CheckAddressUnitTest(v); err == false { //unit test
|
||||
panic(err)
|
||||
}
|
||||
count++
|
||||
}
|
||||
fmt.Println("TestAddress:success test num:", count)
|
||||
}
|
||||
|
||||
func TestAddress_UnmarshalJSON(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
initString := "89aea23276a4090fc2920b788d114d1e96b0fe1d00000003"
|
||||
bytes, err := hex.DecodeString(initString)
|
||||
assert.NoError(t, err)
|
||||
targetAddress, err := CreatAddressFromBytes(bytes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, initString, targetAddress.ToHex()[2:])
|
||||
|
||||
unmarshalData := `"0x89aea23276a4090fc2920b788d114d1e96b0fe1d00000003"` //read from file
|
||||
newAddr := new(Address)
|
||||
err = json.Unmarshal([]byte(unmarshalData), newAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, targetAddress, *newAddr)
|
||||
|
||||
id1, err := CreatRandomIdentity()
|
||||
assert.NoError(t, err)
|
||||
addrBefore := CreatAddressFromIdentity(id1, 3)
|
||||
marshalData, err := json.Marshal(addrBefore)
|
||||
assert.NoError(t, err)
|
||||
addrCheck := new(Address)
|
||||
err = json.Unmarshal(marshalData, addrCheck)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, addrBefore, *addrCheck)
|
||||
}
|
||||
61
qkc/account/branch.go
Normal file
61
qkc/account/branch.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/qkc/common"
|
||||
)
|
||||
|
||||
// Branch branch include it's value
|
||||
type Branch struct {
|
||||
// TODO Value->value
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// NewBranch new branch with value
|
||||
func NewBranch(value uint32) Branch {
|
||||
return Branch{
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// GetChainID get branch's chainID
|
||||
func (Self *Branch) GetChainID() uint32 {
|
||||
return Self.Value >> 16
|
||||
}
|
||||
|
||||
// GetShardSize get branch's shardSize
|
||||
func (Self *Branch) GetShardSize() uint32 {
|
||||
branchValue := Self.Value & ((1 << 16) - 1)
|
||||
return 1 << (common.IntLeftMostBit(branchValue) - 1)
|
||||
}
|
||||
|
||||
// GetFullShardID get branch's fullShardId
|
||||
func (Self *Branch) GetFullShardID() uint32 {
|
||||
return Self.Value
|
||||
}
|
||||
|
||||
// GetShardID get branch branch's shardID
|
||||
func (Self *Branch) GetShardID() uint32 {
|
||||
branchValue := Self.Value & ((1 << 16) - 1)
|
||||
return branchValue ^ Self.GetShardSize()
|
||||
}
|
||||
|
||||
// IsInBranch check shardKey is in current branch
|
||||
func (Self *Branch) IsInBranch(fullShardKey uint32) bool {
|
||||
chainIDMatch := (fullShardKey >> 16) == Self.GetChainID()
|
||||
if chainIDMatch == false {
|
||||
return false
|
||||
}
|
||||
return (fullShardKey & (Self.GetShardSize() - 1)) == Self.GetShardID()
|
||||
}
|
||||
|
||||
// CreatBranch create branch depend shardSize and shardID
|
||||
func CreatBranch(chainID uint32, shardSize uint32, shardID uint32) (Branch, error) {
|
||||
if common.IsP2(shardSize) == false {
|
||||
return Branch{}, errors.New("shardSize is not correct")
|
||||
}
|
||||
return NewBranch(chainID<<16 | shardSize | shardID), nil
|
||||
}
|
||||
120
qkc/account/branch_test.go
Normal file
120
qkc/account/branch_test.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type JSONStruct struct {
|
||||
}
|
||||
|
||||
func NewJSONStruct() *JSONStruct {
|
||||
return &JSONStruct{}
|
||||
}
|
||||
|
||||
func (jst *JSONStruct) Load(filename string, v interface{}) error {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read file failed err %v", err)
|
||||
}
|
||||
err = json.Unmarshal(data, v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unmarshal data failed err %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BranchTestStruct struct {
|
||||
Size uint32 `json:"size"`
|
||||
Key uint32 `json:"key"`
|
||||
TestIsInBranch uint32 `json:"testIsinBranch"`
|
||||
ChainID uint32 `json:"chainID"`
|
||||
GetSize uint32 `json:"getsize"`
|
||||
FullShardID uint32 `json:"fullshardid"`
|
||||
ShardID uint32 `json:"shardid"`
|
||||
IsInBranch bool `json:"isinbranch"`
|
||||
}
|
||||
|
||||
func CheckBranchUnitTest(data BranchTestStruct) bool {
|
||||
tempBranch, err := CreatBranch(0, data.Size, data.Key) //create branch depend on special size ans key
|
||||
if err != nil {
|
||||
fmt.Printf("CreatBranch err %v\n", err)
|
||||
return false
|
||||
}
|
||||
if tempBranch.GetChainID() != data.ChainID { //checkGetChainID
|
||||
fmt.Printf("chainId is not match: unexcepted %d,except %d\n", tempBranch.GetChainID(), data.ChainID)
|
||||
return false
|
||||
}
|
||||
if tempBranch.GetShardSize() != data.GetSize { //checkGetShardSize
|
||||
fmt.Printf("ShardSze is not match: unexcepted %d,excepted %d\n", tempBranch.GetShardSize(), data.GetSize)
|
||||
return false
|
||||
}
|
||||
|
||||
if tempBranch.GetShardID() != data.ShardID { //checkGetShardID
|
||||
fmt.Printf("shardid is not match: unexcepted %d,excepted %d\n", tempBranch.GetShardID(), data.ShardID)
|
||||
return false
|
||||
}
|
||||
|
||||
if tempBranch.IsInBranch(data.TestIsInBranch) != data.IsInBranch { //checkIsInBranch
|
||||
fmt.Printf("isInBranch is not match: unexcepted %t,excepted %t\n", tempBranch.IsInBranch(data.TestIsInBranch), data.IsInBranch)
|
||||
return false
|
||||
}
|
||||
|
||||
if tempBranch.GetFullShardID() != data.FullShardID { //checkGetFullShardID
|
||||
fmt.Printf("full shard id is not match: unexcepted %d,excepted %d\n", tempBranch.GetFullShardID(), data.FullShardID)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 1.python generate testdata
|
||||
//
|
||||
// 1.1 branch from size and key
|
||||
//
|
||||
// 2.go.exe to check
|
||||
//
|
||||
// 2.1 checkGetChainID
|
||||
// 2.2 checkGetShardSize
|
||||
// 2.3 checkGetShardID
|
||||
// 2.4 checkIsInBranch
|
||||
// 2.5 checkGetFullShardID
|
||||
func TestBranch(t *testing.T) {
|
||||
JSONParse := NewJSONStruct()
|
||||
v := []BranchTestStruct{}
|
||||
err := JSONParse.Load("./testdata/testBranch.json", &v) //analysis test data
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
count := 0
|
||||
for _, v := range v {
|
||||
status := CheckBranchUnitTest(v) //unit test
|
||||
if status == false {
|
||||
panic(errors.New("testFailed"))
|
||||
}
|
||||
count++
|
||||
}
|
||||
fmt.Println("TestBranch:success test num:", count)
|
||||
}
|
||||
|
||||
func TestCreatBranch(t *testing.T) {
|
||||
b, err := CreatBranch(3, 8, 6)
|
||||
if err != nil {
|
||||
t.Fatal("CreatBranch error: ", err)
|
||||
}
|
||||
|
||||
check := func(f string, got, want interface{}) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
|
||||
}
|
||||
}
|
||||
check("GetChainID", int(b.GetChainID()), 3)
|
||||
check("GetShardSize", int(b.GetShardSize()), 8)
|
||||
check("GetShardID", int(b.GetShardID()), 6)
|
||||
|
||||
}
|
||||
75
qkc/account/common.go
Normal file
75
qkc/account/common.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// Uint32ToBytes trans uint32 num to bytes
|
||||
func Uint32ToBytes(n uint32) []byte {
|
||||
Bytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(Bytes, n)
|
||||
return Bytes
|
||||
}
|
||||
|
||||
func writeTemporaryKeyFile(file string, content []byte) (string, error) {
|
||||
const dirPerm = 0700
|
||||
if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := f.Write(content); err != nil {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
return "", err
|
||||
}
|
||||
f.Close()
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
func writeKeyFile(file string, content []byte) error {
|
||||
name, err := writeTemporaryKeyFile(file, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(name, file)
|
||||
}
|
||||
|
||||
func ensureInt(x interface{}) int {
|
||||
res, ok := x.(int)
|
||||
if !ok {
|
||||
res = int(x.(float64))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
|
||||
aesBlock, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stream := cipher.NewCTR(aesBlock, iv)
|
||||
outText := make([]byte, len(inText))
|
||||
stream.XORKeyStream(outText, inText)
|
||||
return outText, err
|
||||
}
|
||||
|
||||
// PublicKeyToRecipient publicKey to recipient
|
||||
func PublicKeyToRecipient(p ecdsa.PublicKey) Recipient {
|
||||
recipient := crypto.Keccak256(crypto.FromECDSAPub(&p)[1:])
|
||||
recipientType := BytesToIdentityRecipient(recipient[(len(recipient) - RecipientLength):])
|
||||
return recipientType
|
||||
}
|
||||
102
qkc/account/identity.go
Normal file
102
qkc/account/identity.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// Identity include recipient and key
|
||||
type Identity struct {
|
||||
recipient Recipient
|
||||
key Key
|
||||
}
|
||||
|
||||
// NewIdentity new identity include recipient and key
|
||||
func NewIdentity(recipient Recipient, key Key) Identity {
|
||||
return Identity{
|
||||
recipient: recipient,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
// CreatRandomIdentity create a random identity
|
||||
func CreatRandomIdentity() (Identity, error) {
|
||||
sk, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||
if err != nil {
|
||||
return Identity{}, ErrGenIdentityKey
|
||||
}
|
||||
|
||||
key := crypto.FromECDSA(sk)
|
||||
if len(key) != KeyLength {
|
||||
return Identity{}, fmt.Errorf("privateKey To Bytes falied: unexceptd %d ,excepted 32", len(key))
|
||||
}
|
||||
if len(crypto.FromECDSAPub(&sk.PublicKey)) != 2*KeyLength+1 {
|
||||
return Identity{}, fmt.Errorf("fromECDSAPub len is not match :unexcepted %d,excepted 65", len(crypto.FromECDSAPub(&sk.PublicKey)))
|
||||
}
|
||||
|
||||
recipient := crypto.Keccak256(crypto.FromECDSAPub(&sk.PublicKey)[1:])
|
||||
if len(recipient) != KeyLength {
|
||||
return Identity{}, fmt.Errorf("recipient len is not match:unexceptd %d,exceptd 32", len(recipient))
|
||||
}
|
||||
return newIdentity(recipient, key)
|
||||
|
||||
}
|
||||
|
||||
// CreatIdentityFromKey creat identity from key
|
||||
func CreatIdentityFromKey(key Key) (Identity, error) {
|
||||
keyValue := big.NewInt(0)
|
||||
keyValue.SetBytes(key.Bytes())
|
||||
sk := new(ecdsa.PrivateKey)
|
||||
sk.PublicKey.Curve = crypto.S256()
|
||||
sk.D = keyValue
|
||||
sk.PublicKey.X, sk.PublicKey.Y = crypto.S256().ScalarBaseMult(keyValue.Bytes())
|
||||
if len(crypto.FromECDSAPub(&sk.PublicKey)) != 2*KeyLength+1 {
|
||||
return Identity{}, fmt.Errorf("fromECDSAPub len is not match :unexcepted %d,excepted %d", len(crypto.FromECDSAPub(&sk.PublicKey)), 2*KeyLength+1)
|
||||
}
|
||||
|
||||
recipient := crypto.Keccak256(crypto.FromECDSAPub(&sk.PublicKey)[1:]) //"0x04"+64
|
||||
if len(recipient) != KeyLength {
|
||||
return Identity{}, fmt.Errorf("recipient len is not match:unexceptd %d,exceptd 32", len(recipient))
|
||||
}
|
||||
|
||||
return newIdentity(recipient, key.Bytes())
|
||||
}
|
||||
|
||||
func newIdentity(recipient []byte, key []byte) (Identity, error) {
|
||||
recipientType := BytesToIdentityRecipient(recipient[(len(recipient) - RecipientLength):])
|
||||
keyType := BytesToIdentityKey(key)
|
||||
return NewIdentity(recipientType, keyType), nil
|
||||
}
|
||||
|
||||
// GetDefaultFullShardKey get identity's default fullShardKey
|
||||
func (Self *Identity) GetDefaultFullShardKey() (uint32, error) {
|
||||
var fullShardKey uint32
|
||||
r := Self.recipient
|
||||
realShardKey := []byte{0x00, 0x00}
|
||||
realShardKey = append(realShardKey, r[0:1]...)
|
||||
realShardKey = append(realShardKey, r[10:11]...)
|
||||
buffer := bytes.NewBuffer(realShardKey)
|
||||
err := binary.Read(buffer, binary.BigEndian, &fullShardKey)
|
||||
if err != nil {
|
||||
return fullShardKey, err
|
||||
}
|
||||
return fullShardKey, nil
|
||||
}
|
||||
|
||||
// GetRecipient Get it's recipient
|
||||
func (Self *Identity) GetRecipient() Recipient {
|
||||
return Self.recipient
|
||||
}
|
||||
|
||||
// GetKey get it's key
|
||||
func (Self *Identity) GetKey() Key {
|
||||
return Self.key
|
||||
}
|
||||
148
qkc/account/identity_test.go
Normal file
148
qkc/account/identity_test.go
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
type IdentityTestStruct struct {
|
||||
Key string `json:"key"`
|
||||
IDKey string `json:"idkey"`
|
||||
Recipient string `json:"recipient"`
|
||||
}
|
||||
|
||||
func CheckIdentityUnitTest(data IdentityTestStruct) bool {
|
||||
key, err := hex.DecodeString(data.Key)
|
||||
if err != nil {
|
||||
fmt.Println("DecodeString failed err", err)
|
||||
return false
|
||||
}
|
||||
keyType := BytesToIdentityKey(key)
|
||||
identity, err := CreatIdentityFromKey(keyType)
|
||||
if err := checkPublicToRecipient(keyType, identity.recipient); err != nil {
|
||||
fmt.Println("checkPublicToRecipient err", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("creatFromKey Failed err", err)
|
||||
return false
|
||||
}
|
||||
if hex.EncodeToString(identity.recipient.Bytes()) != data.Recipient { //checkRecipent
|
||||
fmt.Printf("recipient is not match : unexcepted:%s , excepted %s", hex.EncodeToString(identity.recipient.Bytes()), data.Recipient)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkPublicToRecipient(key Key, recipient Recipient) error {
|
||||
keyValue := big.NewInt(0)
|
||||
keyValue.SetBytes(key.Bytes())
|
||||
sk := new(ecdsa.PrivateKey)
|
||||
sk.PublicKey.Curve = crypto.S256()
|
||||
sk.D = keyValue
|
||||
sk.PublicKey.X, sk.PublicKey.Y = crypto.S256().ScalarBaseMult(keyValue.Bytes())
|
||||
|
||||
recipientData := PublicKeyToRecipient(sk.PublicKey)
|
||||
if bytes.Equal(recipientData.Bytes(), recipient.Bytes()) {
|
||||
return nil
|
||||
}
|
||||
return errors.New("check public to recipient failed")
|
||||
}
|
||||
|
||||
// 1.python generate testdata
|
||||
//
|
||||
// 1.1 identity from key
|
||||
//
|
||||
// 2.go.exe to check
|
||||
//
|
||||
// 2.1 checkRecipent
|
||||
func TestIdentity(t *testing.T) {
|
||||
JSONParse := NewJSONStruct()
|
||||
v := []IdentityTestStruct{}
|
||||
err := JSONParse.Load("./testdata/testIdentity.json", &v) //analysis test data
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
count := 0
|
||||
for _, v := range v {
|
||||
err := CheckIdentityUnitTest(v) //unit test
|
||||
if err == false {
|
||||
panic(err)
|
||||
}
|
||||
count++
|
||||
}
|
||||
fmt.Println("TestIdentity:success test num:", count)
|
||||
}
|
||||
|
||||
type TestBytesTo struct {
|
||||
recipient []byte
|
||||
key []byte
|
||||
exceptedRecipient *big.Int
|
||||
exceptedKey *big.Int
|
||||
}
|
||||
|
||||
func TestBytesToIdentityKey(t *testing.T) {
|
||||
testCase := []TestBytesTo{
|
||||
{
|
||||
recipient: []byte{0x1},
|
||||
key: []byte{0x2},
|
||||
exceptedRecipient: new(big.Int).SetBytes([]byte{0x1}),
|
||||
exceptedKey: new(big.Int).SetBytes([]byte{0x2}),
|
||||
},
|
||||
{
|
||||
recipient: []byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1},
|
||||
key: []byte{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2},
|
||||
exceptedRecipient: new(big.Int).SetBytes([]byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}),
|
||||
exceptedKey: new(big.Int).SetBytes([]byte{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2}),
|
||||
},
|
||||
{
|
||||
recipient: []byte{0x66, 0x77, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x3},
|
||||
key: []byte{0x66, 0x77, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x4},
|
||||
exceptedRecipient: new(big.Int).SetBytes([]byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x3}),
|
||||
exceptedKey: new(big.Int).SetBytes([]byte{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x4}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range testCase {
|
||||
recipientValue := BytesToIdentityRecipient(v.recipient)
|
||||
keyValue := BytesToIdentityKey(v.key)
|
||||
if new(big.Int).SetBytes(recipientValue.Bytes()).Cmp(v.exceptedRecipient) != 0 {
|
||||
t.Error("test BytesToIdentityKey failed", "excepted:", v.exceptedRecipient.Bytes(), "get", recipientValue.Bytes())
|
||||
}
|
||||
if new(big.Int).SetBytes(keyValue.Bytes()).Cmp(v.exceptedKey) != 0 {
|
||||
t.Error("test BytesToIdentityKey failed", "excepted:", v.exceptedKey, "get", keyValue.Bytes())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreatIdentity(t *testing.T) {
|
||||
check := func(f string, got, want interface{}) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
|
||||
}
|
||||
}
|
||||
id1, err := CreatRandomIdentity()
|
||||
if err != nil {
|
||||
t.Fatal("CreatRandomIdentity error: ", err)
|
||||
}
|
||||
id2, err := CreatIdentityFromKey(id1.key)
|
||||
if err != nil {
|
||||
t.Fatal("CreatIdentityFromKey error: ", err)
|
||||
}
|
||||
|
||||
check("GetRecipient", id1.GetRecipient(), id2.GetRecipient())
|
||||
check("GetKey", id1.GetKey(), id2.GetKey())
|
||||
}
|
||||
21
qkc/account/testdata/keystore/29e623a5-5175-4bde-a912-3988f86a312c.json
vendored
Normal file
21
qkc/account/testdata/keystore/29e623a5-5175-4bde-a912-3988f86a312c.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "96dfa44e6fb5fd53e799ee99689a50d0c9c3486594e9152314abae7ac4aa5e5e",
|
||||
"cipherparams": {
|
||||
"iv": "741dbd5bfee4fcd871564260e34330b3"
|
||||
},
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {
|
||||
"prf": "hmac-sha256",
|
||||
"dklen": 32,
|
||||
"c": 262144,
|
||||
"salt": "afed67310f19060af0cb4a7231c51269"
|
||||
},
|
||||
"mac": "12eaf7aac9f5351fc91f4ec9766a6a30f75b47523e4b05ec62f026f7ceaebfad",
|
||||
"version": 1
|
||||
},
|
||||
"id": "29e623a5-5175-4bde-a912-3988f86a312c",
|
||||
"version": 3,
|
||||
"address": "d21c6eb2fb40e727b07054556c5cec1d51561f610000d254"
|
||||
}
|
||||
21
qkc/account/testdata/keystore/4acc37a5-127a-4d73-8575-b720fd95c3ad.json
vendored
Normal file
21
qkc/account/testdata/keystore/4acc37a5-127a-4d73-8575-b720fd95c3ad.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "a966d077dbbefdb37b97022ac96bf44198b21cc4b6aa3f63ab1cacacca675d13",
|
||||
"cipherparams": {
|
||||
"iv": "e7f2df3482d8cf89f1bb5426a63ac61f"
|
||||
},
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {
|
||||
"prf": "hmac-sha256",
|
||||
"dklen": 32,
|
||||
"c": 262144,
|
||||
"salt": "c8867b3a857fb7d041b3255f564dd8fa"
|
||||
},
|
||||
"mac": "a286f23f1c41a74e2eb1511e1caf55b829a8d57803001c58be887eed109dd960",
|
||||
"version": 1
|
||||
},
|
||||
"id": "4acc37a5-127a-4d73-8575-b720fd95c3ad",
|
||||
"version": 3,
|
||||
"address": "aa56ae9b8e0c17b6dab87918a75450dfa66de62c0000aa79"
|
||||
}
|
||||
21
qkc/account/testdata/keystore/8f777350-5bb2-4e4a-badf-5036996d2e42.json
vendored
Normal file
21
qkc/account/testdata/keystore/8f777350-5bb2-4e4a-badf-5036996d2e42.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "a8efbc31ca7eee3c312b25de629bddb752f919b265cea32fd53a19ead9504880",
|
||||
"cipherparams": {
|
||||
"iv": "7fac9151565b704671f27aff1dc7395d"
|
||||
},
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {
|
||||
"prf": "hmac-sha256",
|
||||
"dklen": 32,
|
||||
"c": 262144,
|
||||
"salt": "aa47a2580d9e9a310f305b3edd235734"
|
||||
},
|
||||
"mac": "1aa3d11d683d44cb720682d412ff3270c97f85335937d4853eb4624493cf2c1b",
|
||||
"version": 1
|
||||
},
|
||||
"id": "8f777350-5bb2-4e4a-badf-5036996d2e42",
|
||||
"version": 3,
|
||||
"address": "37504025caf65d2ce47f8b07f2737e4167a0f1420000378b"
|
||||
}
|
||||
21
qkc/account/testdata/keystore/c3e1ee18-2ded-43c8-b76d-d59758a831c4.json
vendored
Normal file
21
qkc/account/testdata/keystore/c3e1ee18-2ded-43c8-b76d-d59758a831c4.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "7798615a8878c63696bcbd820968a1c40cb1ac242b008a060e9238a83c0ec5f4",
|
||||
"cipherparams": {
|
||||
"iv": "4ac167b1e21fc454783cd76ec4b14de8"
|
||||
},
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {
|
||||
"prf": "hmac-sha256",
|
||||
"dklen": 32,
|
||||
"c": 262144,
|
||||
"salt": "43c1f1fa963e7a67b303e392fd3ba17a"
|
||||
},
|
||||
"mac": "2b551ee23c3d14c9cc5c30086f17cf9f7c0c2a6304590d11e834ce475b8c40e4",
|
||||
"version": 1
|
||||
},
|
||||
"id": "c3e1ee18-2ded-43c8-b76d-d59758a831c4",
|
||||
"version": 3,
|
||||
"address": "f2b1937a39f9b3ac0e9e4cb873d7f09b2213be5c0000f24c"
|
||||
}
|
||||
21
qkc/account/testdata/keystore/dbc5e27f-7002-4b1c-bef7-e6e2d736652b.json
vendored
Normal file
21
qkc/account/testdata/keystore/dbc5e27f-7002-4b1c-bef7-e6e2d736652b.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "2e4c2ee955acbb50ce72465e7cb48d442158dd2dd9e9e5a94c5ac3dabf177416",
|
||||
"cipherparams": {
|
||||
"iv": "4429e5f48c62c91c3fb1dfaf60e5d714"
|
||||
},
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {
|
||||
"prf": "hmac-sha256",
|
||||
"dklen": 32,
|
||||
"c": 262144,
|
||||
"salt": "8a393151cbbfd9ffaf2bb2101a7564ad"
|
||||
},
|
||||
"mac": "48f28f7b398bbeb89eef432ed40bc93ffa02ec3903fbf20ca4b01df6d36a90ea",
|
||||
"version": 1
|
||||
},
|
||||
"id": "dbc5e27f-7002-4b1c-bef7-e6e2d736652b",
|
||||
"version": 3,
|
||||
"address": "376ee92116da5c7668a2f553369311cbda5adffa000037f5"
|
||||
}
|
||||
1
qkc/account/testdata/testAccount.json
vendored
Normal file
1
qkc/account/testdata/testAccount.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
[{"tKey": "2d7bd0e62149f345975541ee8f18b5c4e247b637dc9275b96977aae3029951f9", "address": "376ee92116da5c7668a2f553369311cbda5adffa000037f5", "privateKey": "2d7bd0e62149f345975541ee8f18b5c4e247b637dc9275b96977aae3029951f9", "uuid": "dbc5e27f-7002-4b1c-bef7-e6e2d736652b"}, {"tKey": "3b5ea1ae1630c2eada7432b1d691beb2f82ba88831dadd6dc7ac177b92562aca", "address": "d21c6eb2fb40e727b07054556c5cec1d51561f610000d254", "privateKey": "3b5ea1ae1630c2eada7432b1d691beb2f82ba88831dadd6dc7ac177b92562aca", "uuid": "29e623a5-5175-4bde-a912-3988f86a312c"}, {"tKey": "cd1ca5ec87a18dd390f3019acb6b97c8b5550e715acbc50a5ccd406d006ce966", "address": "f2b1937a39f9b3ac0e9e4cb873d7f09b2213be5c0000f24c", "privateKey": "cd1ca5ec87a18dd390f3019acb6b97c8b5550e715acbc50a5ccd406d006ce966", "uuid": "c3e1ee18-2ded-43c8-b76d-d59758a831c4"}, {"tKey": "f7aed08b4b01b649e0bf528d34ae1a5342dcac6af0a86a8e439448512d8a2b27", "address": "aa56ae9b8e0c17b6dab87918a75450dfa66de62c0000aa79", "privateKey": "f7aed08b4b01b649e0bf528d34ae1a5342dcac6af0a86a8e439448512d8a2b27", "uuid": "4acc37a5-127a-4d73-8575-b720fd95c3ad"}, {"tKey": "50400b95c32e7faac662c1dd8cccbd03bccdef1c41343ebbc8a6093f50662d14", "address": "37504025caf65d2ce47f8b07f2737e4167a0f1420000378b", "privateKey": "50400b95c32e7faac662c1dd8cccbd03bccdef1c41343ebbc8a6093f50662d14", "uuid": "8f777350-5bb2-4e4a-badf-5036996d2e42"}]
|
||||
1
qkc/account/testdata/testAddress.json
vendored
Normal file
1
qkc/account/testdata/testAddress.json
vendored
Normal file
File diff suppressed because one or more lines are too long
1602
qkc/account/testdata/testBranch.json
vendored
Normal file
1602
qkc/account/testdata/testBranch.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
qkc/account/testdata/testIdentity.json
vendored
Normal file
1
qkc/account/testdata/testIdentity.json
vendored
Normal file
File diff suppressed because one or more lines are too long
91
qkc/account/types.go
Normal file
91
qkc/account/types.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/account (byte-compatible).
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrGenIdentityKey error info : err generate identity key
|
||||
ErrGenIdentityKey = errors.New("ErrGenIdentityKey")
|
||||
)
|
||||
|
||||
// DefaultKeyStoreDirectory default keystore dir
|
||||
const (
|
||||
DefaultKeyStoreDirectory = "./keystore/"
|
||||
kdfParamsPrf = "prf"
|
||||
kdfParamsPrfValue = "hmac-sha256"
|
||||
kdfParamsPrfDkLen = "dklen"
|
||||
kdfParamsPrfDkLenValue = 32
|
||||
kdfParamsC = "c"
|
||||
kdfParamsCValue = 262144
|
||||
kdfParamsSalt = "salt"
|
||||
|
||||
cryptoKDF = "pbkdf2"
|
||||
cryptoCipher = "aes-128-ctr"
|
||||
cryptoVersion = 1
|
||||
|
||||
jsonVersion = 3
|
||||
)
|
||||
|
||||
const (
|
||||
RecipientLength = 20
|
||||
KeyLength = 32
|
||||
FullShardKeyLength = 4
|
||||
)
|
||||
|
||||
// Recipient recipient type
|
||||
type Recipient = common.Address
|
||||
|
||||
// BytesToIdentityRecipient trans bytes to Recipient
|
||||
func BytesToIdentityRecipient(b []byte) Recipient {
|
||||
return Recipient(common.BytesToAddress(b))
|
||||
}
|
||||
|
||||
func IsSameReceipt(a, b Recipient) bool {
|
||||
for index := 0; index < 20; index++ {
|
||||
if a[index] != b[index] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func IsSameAddress(a, b Address) bool {
|
||||
return IsSameReceipt(a.Recipient, b.Recipient) && a.FullShardKey == b.FullShardKey
|
||||
}
|
||||
|
||||
// Key key type
|
||||
type Key [KeyLength]byte
|
||||
|
||||
// SetBytes set bytes to it's value
|
||||
func (a *Key) SetBytes(b []byte) {
|
||||
if len(b) > len(a) {
|
||||
b = b[len(b)-KeyLength:]
|
||||
}
|
||||
copy(a[KeyLength-len(b):], b)
|
||||
}
|
||||
|
||||
// Bytes return it's bytes
|
||||
func (a Key) Bytes() []byte {
|
||||
return a[:]
|
||||
}
|
||||
|
||||
// BytesToIdentityKey trans bytes to Key
|
||||
func BytesToIdentityKey(b []byte) Key {
|
||||
var a Key
|
||||
a.SetBytes(b)
|
||||
return a
|
||||
}
|
||||
|
||||
type CoinbaseStatses struct {
|
||||
CoinbaseStatsList []CoinbaseStats `json:"ReceiptCntList" gencodec:"required" bytesizeofslicelen:"4"`
|
||||
}
|
||||
|
||||
type CoinbaseStats struct {
|
||||
Addr Recipient
|
||||
Cnt uint32
|
||||
}
|
||||
243
qkc/common/hexutil/hexutil.go
Normal file
243
qkc/common/hexutil/hexutil.go
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common/hexutil
|
||||
// (a fork of geth's hexutil with the leading-zero checks disabled).
|
||||
//
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
Package hexutil implements hex encoding with 0x prefix.
|
||||
This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads.
|
||||
|
||||
# Encoding Rules
|
||||
|
||||
All hex data must have prefix "0x".
|
||||
|
||||
For byte slices, the hex data must be of even length. An empty byte slice
|
||||
encodes as "0x".
|
||||
|
||||
Integers are encoded using the least amount of digits (no leading zero digits). Their
|
||||
encoding may be of uneven length. The number zero encodes as "0x0".
|
||||
*/
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const uintBits = 32 << (uint64(^uint(0)) >> 63)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrEmptyString = &decError{"empty hex string"}
|
||||
ErrSyntax = &decError{"invalid hex string"}
|
||||
ErrMissingPrefix = &decError{"hex string without 0x prefix"}
|
||||
ErrOddLength = &decError{"hex string of odd length"}
|
||||
ErrEmptyNumber = &decError{"hex string \"0x\""}
|
||||
ErrLeadingZero = &decError{"hex number with leading zero digits"}
|
||||
ErrUint64Range = &decError{"hex number > 64 bits"}
|
||||
ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)}
|
||||
ErrBig256Range = &decError{"hex number > 256 bits"}
|
||||
)
|
||||
|
||||
type decError struct{ msg string }
|
||||
|
||||
func (err decError) Error() string { return err.msg }
|
||||
|
||||
// Decode decodes a hex string with 0x prefix.
|
||||
func Decode(input string) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
b, err := hex.DecodeString(input[2:])
|
||||
if err != nil {
|
||||
err = mapError(err)
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
|
||||
func MustDecode(input string) []byte {
|
||||
dec, err := Decode(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// Encode encodes b as a hex string with 0x prefix.
|
||||
func Encode(b []byte) string {
|
||||
enc := make([]byte, len(b)*2+2)
|
||||
copy(enc, "0x")
|
||||
hex.Encode(enc[2:], b)
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
// DecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
func DecodeUint64(input string) (uint64, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dec, err := strconv.ParseUint(raw, 16, 64)
|
||||
if err != nil {
|
||||
err = mapError(err)
|
||||
}
|
||||
return dec, err
|
||||
}
|
||||
|
||||
// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeUint64(input string) uint64 {
|
||||
dec, err := DecodeUint64(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeUint64 encodes i as a hex string with 0x prefix.
|
||||
func EncodeUint64(i uint64) string {
|
||||
enc := make([]byte, 2, 10)
|
||||
copy(enc, "0x")
|
||||
return string(strconv.AppendUint(enc, i, 16))
|
||||
}
|
||||
|
||||
var bigWordNibbles int
|
||||
|
||||
func init() {
|
||||
// This is a weird way to compute the number of nibbles required for big.Word.
|
||||
// The usual way would be to use constant arithmetic but go vet can't handle that.
|
||||
b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
|
||||
switch len(b.Bits()) {
|
||||
case 1:
|
||||
bigWordNibbles = 16
|
||||
case 2:
|
||||
bigWordNibbles = 8
|
||||
default:
|
||||
panic("weird big.Word size")
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
// Numbers larger than 256 bits are not accepted.
|
||||
func DecodeBig(input string) (*big.Int, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) > 64 {
|
||||
return nil, ErrBig256Range
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return nil, ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
dec := new(big.Int).SetBits(words)
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
// MustDecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeBig(input string) *big.Int {
|
||||
dec, err := DecodeBig(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeBig encodes bigint as a hex string with 0x prefix.
|
||||
// The sign of the integer is ignored.
|
||||
func EncodeBig(bigint *big.Int) string {
|
||||
nbits := bigint.BitLen()
|
||||
if nbits == 0 {
|
||||
return "0x0"
|
||||
}
|
||||
return fmt.Sprintf("%#x", bigint)
|
||||
}
|
||||
|
||||
func has0xPrefix(input string) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkNumber(input string) (raw string, err error) {
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return "", ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyNumber
|
||||
}
|
||||
//if len(input) > 1 && input[0] == '0' {
|
||||
// return "", ErrLeadingZero
|
||||
//}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
const badNibble = ^uint64(0)
|
||||
|
||||
func decodeNibble(in byte) uint64 {
|
||||
switch {
|
||||
case in >= '0' && in <= '9':
|
||||
return uint64(in - '0')
|
||||
case in >= 'A' && in <= 'F':
|
||||
return uint64(in - 'A' + 10)
|
||||
case in >= 'a' && in <= 'f':
|
||||
return uint64(in - 'a' + 10)
|
||||
default:
|
||||
return badNibble
|
||||
}
|
||||
}
|
||||
|
||||
func mapError(err error) error {
|
||||
if err, ok := err.(*strconv.NumError); ok {
|
||||
switch err.Err {
|
||||
case strconv.ErrRange:
|
||||
return ErrUint64Range
|
||||
case strconv.ErrSyntax:
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
if _, ok := err.(hex.InvalidByteError); ok {
|
||||
return ErrSyntax
|
||||
}
|
||||
if err == hex.ErrLength {
|
||||
return ErrOddLength
|
||||
}
|
||||
return err
|
||||
}
|
||||
210
qkc/common/hexutil/hexutil_test.go
Normal file
210
qkc/common/hexutil/hexutil_test.go
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common/hexutil
|
||||
// (tests modified upstream to assert the lenient leading-zero behaviour).
|
||||
//
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type marshalTest struct {
|
||||
input interface{}
|
||||
want string
|
||||
}
|
||||
|
||||
type unmarshalTest struct {
|
||||
input string
|
||||
want interface{}
|
||||
wantErr error // if set, decoding must fail on any platform
|
||||
wantErr32bit error // if set, decoding must fail on 32bit platforms (used for Uint tests)
|
||||
}
|
||||
|
||||
var (
|
||||
encodeBytesTests = []marshalTest{
|
||||
{[]byte{}, "0x"},
|
||||
{[]byte{0}, "0x00"},
|
||||
{[]byte{0, 0, 1, 2}, "0x00000102"},
|
||||
}
|
||||
|
||||
encodeBigTests = []marshalTest{
|
||||
{referenceBig("0"), "0x0"},
|
||||
{referenceBig("1"), "0x1"},
|
||||
{referenceBig("ff"), "0xff"},
|
||||
{referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
|
||||
{referenceBig("80a7f2c1bcc396c00"), "0x80a7f2c1bcc396c00"},
|
||||
{referenceBig("-80a7f2c1bcc396c00"), "-0x80a7f2c1bcc396c00"},
|
||||
}
|
||||
|
||||
encodeUint64Tests = []marshalTest{
|
||||
{uint64(0), "0x0"},
|
||||
{uint64(1), "0x1"},
|
||||
{uint64(0xff), "0xff"},
|
||||
{uint64(0x1122334455667788), "0x1122334455667788"},
|
||||
}
|
||||
|
||||
encodeUintTests = []marshalTest{
|
||||
{uint(0), "0x0"},
|
||||
{uint(1), "0x1"},
|
||||
{uint(0xff), "0xff"},
|
||||
{uint(0x11223344), "0x11223344"},
|
||||
}
|
||||
|
||||
decodeBytesTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: ``, wantErr: ErrEmptyString},
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x0`, wantErr: ErrOddLength},
|
||||
{input: `0x023`, wantErr: ErrOddLength},
|
||||
{input: `0xxx`, wantErr: ErrSyntax},
|
||||
{input: `0x01zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x`, want: []byte{}},
|
||||
{input: `0X`, want: []byte{}},
|
||||
{input: `0x02`, want: []byte{0x02}},
|
||||
{input: `0X02`, want: []byte{0x02}},
|
||||
{input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
}
|
||||
|
||||
decodeBigTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
{
|
||||
input: `0x10000000000000000000000000000000000000000000000000000000000000000`,
|
||||
wantErr: ErrBig256Range,
|
||||
},
|
||||
// valid
|
||||
{input: `0x0`, want: big.NewInt(0)},
|
||||
{input: `0x01`, want: big.NewInt(0x01)},
|
||||
{input: `0x001`, want: big.NewInt(0x001)},
|
||||
{input: `0x0001`, want: big.NewInt(0x0001)},
|
||||
{input: `0x2`, want: big.NewInt(0x2)},
|
||||
{input: `0x2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0X2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0x1122aaff`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `0xbBb`, want: big.NewInt(0xbbb)},
|
||||
{input: `0xfffffffff`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `0x112233445566778899aabbccddeeff`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
decodeUint64Tests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0xfffffffffffffffff`, wantErr: ErrUint64Range},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x0`, want: uint64(0)},
|
||||
{input: `0x01`, want: uint64(0x01)},
|
||||
{input: `0x001`, want: uint64(0x001)},
|
||||
{input: `0x0001`, want: uint64(0x0001)},
|
||||
{input: `0x2`, want: uint64(0x2)},
|
||||
{input: `0x2F2`, want: uint64(0x2f2)},
|
||||
{input: `0X2F2`, want: uint64(0x2f2)},
|
||||
{input: `0x1122aaff`, want: uint64(0x1122aaff)},
|
||||
{input: `0xbbb`, want: uint64(0xbbb)},
|
||||
{input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
enc := Encode(test.input.([]byte))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
for _, test := range decodeBytesTests {
|
||||
dec, err := Decode(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), dec) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
enc := EncodeBig(test.input.(*big.Int))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBig(t *testing.T) {
|
||||
for _, test := range decodeBigTests {
|
||||
dec, err := DecodeBig(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec.Cmp(test.want.(*big.Int)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
enc := EncodeUint64(test.input.(uint64))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeUint64(t *testing.T) {
|
||||
for _, test := range decodeUint64Tests {
|
||||
dec, err := DecodeUint64(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
329
qkc/common/hexutil/json.go
Normal file
329
qkc/common/hexutil/json.go
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common/hexutil
|
||||
// (a fork of geth's hexutil with the leading-zero checks disabled).
|
||||
//
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
bytesT = reflect.TypeOf(Bytes(nil))
|
||||
bigT = reflect.TypeOf((*Big)(nil))
|
||||
uintT = reflect.TypeOf(Uint(0))
|
||||
uint64T = reflect.TypeOf(Uint64(0))
|
||||
)
|
||||
|
||||
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The empty slice marshals as "0x".
|
||||
type Bytes []byte
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler
|
||||
func (b Bytes) MarshalText() ([]byte, error) {
|
||||
result := make([]byte, len(b)*2+2)
|
||||
copy(result, `0x`)
|
||||
hex.Encode(result[2:], b)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
||||
if string(input) == "null" {
|
||||
dec := make([]byte, 0)
|
||||
*b = dec
|
||||
return nil
|
||||
}
|
||||
if !isString(input) {
|
||||
return errNonString(bytesT)
|
||||
}
|
||||
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bytesT)
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (b *Bytes) UnmarshalText(input []byte) error {
|
||||
raw, err := checkText(input, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec := make([]byte, len(raw)/2)
|
||||
if _, err = hex.Decode(dec, raw); err != nil {
|
||||
err = mapError(err)
|
||||
} else {
|
||||
*b = dec
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Bytes) String() string {
|
||||
return Encode(b)
|
||||
}
|
||||
|
||||
// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
|
||||
// determines the required input length. This function is commonly used to implement the
|
||||
// UnmarshalJSON method for fixed-size types.
|
||||
func UnmarshalFixedJSON(typ reflect.Type, input, out []byte) error {
|
||||
if !isString(input) {
|
||||
return errNonString(typ)
|
||||
}
|
||||
return wrapTypeError(UnmarshalFixedText(typ.String(), input[1:len(input)-1], out), typ)
|
||||
}
|
||||
|
||||
// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out
|
||||
// determines the required input length. This function is commonly used to implement the
|
||||
// UnmarshalText method for fixed-size types.
|
||||
func UnmarshalFixedText(typname string, input, out []byte) error {
|
||||
raw, err := checkText(input, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw)/2 != len(out) {
|
||||
return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
|
||||
}
|
||||
// Pre-verify syntax before modifying out.
|
||||
for _, b := range raw {
|
||||
if decodeNibble(b) == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
hex.Decode(out, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalFixedUnprefixedText decodes the input as a string with optional 0x prefix. The
|
||||
// length of out determines the required input length. This function is commonly used to
|
||||
// implement the UnmarshalText method for fixed-size types.
|
||||
func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error {
|
||||
raw, err := checkText(input, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw)/2 != len(out) {
|
||||
return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
|
||||
}
|
||||
// Pre-verify syntax before modifying out.
|
||||
for _, b := range raw {
|
||||
if decodeNibble(b) == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
hex.Decode(out, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Big marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
//
|
||||
// Negative integers are not supported at this time. Attempting to marshal them will
|
||||
// return an error. Values larger than 256bits are rejected by Unmarshal but will be
|
||||
// marshaled without error.
|
||||
type Big big.Int
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler
|
||||
func (b Big) MarshalText() ([]byte, error) {
|
||||
return []byte(EncodeBig((*big.Int)(&b))), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Big) UnmarshalJSON(input []byte) error {
|
||||
if !isString(input) {
|
||||
return errNonString(bigT)
|
||||
}
|
||||
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bigT)
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler
|
||||
func (b *Big) UnmarshalText(input []byte) error {
|
||||
raw, err := checkNumberText(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw) > 64 {
|
||||
return ErrBig256Range
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
var dec big.Int
|
||||
dec.SetBits(words)
|
||||
*b = (Big)(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToInt converts b to a big.Int.
|
||||
func (b *Big) ToInt() *big.Int {
|
||||
return (*big.Int)(b)
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b *Big) String() string {
|
||||
return EncodeBig(b.ToInt())
|
||||
}
|
||||
|
||||
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint64 uint64
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (b Uint64) MarshalText() ([]byte, error) {
|
||||
buf := make([]byte, 2, 10)
|
||||
copy(buf, `0x`)
|
||||
buf = strconv.AppendUint(buf, uint64(b), 16)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
||||
if !isString(input) {
|
||||
return errNonString(uint64T)
|
||||
}
|
||||
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uint64T)
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler
|
||||
func (b *Uint64) UnmarshalText(input []byte) error {
|
||||
raw, err := checkNumberText(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw) > 16 {
|
||||
return ErrUint64Range
|
||||
}
|
||||
var dec uint64
|
||||
for _, byte := range raw {
|
||||
nib := decodeNibble(byte)
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
dec *= 16
|
||||
dec += nib
|
||||
}
|
||||
*b = Uint64(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint64) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
// Uint marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint uint
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (b Uint) MarshalText() ([]byte, error) {
|
||||
return Uint64(b).MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint) UnmarshalJSON(input []byte) error {
|
||||
if !isString(input) {
|
||||
return errNonString(uintT)
|
||||
}
|
||||
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uintT)
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (b *Uint) UnmarshalText(input []byte) error {
|
||||
var u64 Uint64
|
||||
err := u64.UnmarshalText(input)
|
||||
if u64 > Uint64(^uint(0)) || err == ErrUint64Range {
|
||||
return ErrUintRange
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = Uint(u64)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
func isString(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
|
||||
}
|
||||
|
||||
func bytesHave0xPrefix(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkText(input []byte, wantPrefix bool) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, nil // empty strings are allowed
|
||||
}
|
||||
if bytesHave0xPrefix(input) {
|
||||
input = input[2:]
|
||||
} else if wantPrefix {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
if len(input)%2 != 0 {
|
||||
return nil, ErrOddLength
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func checkNumberText(input []byte) (raw []byte, err error) {
|
||||
if len(input) == 0 {
|
||||
return nil, nil // empty strings are allowed
|
||||
}
|
||||
if !bytesHave0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyNumber
|
||||
}
|
||||
//if len(input) > 1 && input[0] == '0' {
|
||||
// return nil, ErrLeadingZero
|
||||
//}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func wrapTypeError(err error, typ reflect.Type) error {
|
||||
if _, ok := err.(*decError); ok {
|
||||
return &json.UnmarshalTypeError{Value: err.Error(), Type: typ}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func errNonString(typ reflect.Type) error {
|
||||
return &json.UnmarshalTypeError{Value: "non-string", Type: typ}
|
||||
}
|
||||
48
qkc/common/hexutil/json_example_test.go
Normal file
48
qkc/common/hexutil/json_example_test.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common/hexutil
|
||||
// (import path rewritten).
|
||||
//
|
||||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/qkc/common/hexutil"
|
||||
)
|
||||
|
||||
type MyType [5]byte
|
||||
|
||||
func (v *MyType) UnmarshalText(input []byte) error {
|
||||
return hexutil.UnmarshalFixedText("MyType", input, v[:])
|
||||
}
|
||||
|
||||
func (v MyType) String() string {
|
||||
return hexutil.Bytes(v[:]).String()
|
||||
}
|
||||
|
||||
func ExampleUnmarshalFixedText() {
|
||||
var v1, v2 MyType
|
||||
fmt.Println("v1 error:", json.Unmarshal([]byte(`"0x01"`), &v1))
|
||||
fmt.Println("v2 error:", json.Unmarshal([]byte(`"0x0101010101"`), &v2))
|
||||
fmt.Println("v2:", v2)
|
||||
// Output:
|
||||
// v1 error: hex string has length 2, want 10 for MyType
|
||||
// v2 error: <nil>
|
||||
// v2: 0x0101010101
|
||||
}
|
||||
382
qkc/common/hexutil/json_test.go
Normal file
382
qkc/common/hexutil/json_test.go
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common/hexutil
|
||||
// (tests modified upstream to assert the lenient leading-zero behaviour).
|
||||
//
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func checkError(t *testing.T, input string, got, want error) bool {
|
||||
if got == nil {
|
||||
if want != nil {
|
||||
t.Errorf("input %s: got no error, want %q", input, want)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if want == nil {
|
||||
t.Errorf("input %s: unexpected error %q", input, got)
|
||||
} else if got.Error() != want.Error() {
|
||||
t.Errorf("input %s: got error %q, want %q", input, got, want)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func referenceBig(s string) *big.Int {
|
||||
b, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic("invalid")
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func referenceBytes(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
var errJSONEOF = errors.New("unexpected end of JSON input")
|
||||
|
||||
var unmarshalBytesTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errJSONEOF},
|
||||
{input: "10", wantErr: errNonString(bytesT)},
|
||||
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bytesT)},
|
||||
{input: `"0x0"`, wantErr: wrapTypeError(ErrOddLength, bytesT)},
|
||||
{input: `"0xxx"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
|
||||
{input: `"0x01zz01"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
|
||||
|
||||
// valid encoding
|
||||
{input: `""`, want: referenceBytes("")},
|
||||
{input: `"0x"`, want: referenceBytes("")},
|
||||
{input: `"0x02"`, want: referenceBytes("02")},
|
||||
{input: `"0X02"`, want: referenceBytes("02")},
|
||||
{input: `"0xffffffffff"`, want: referenceBytes("ffffffffff")},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBytes("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBytes(t *testing.T) {
|
||||
for _, test := range unmarshalBytesTests {
|
||||
var v Bytes
|
||||
err := json.Unmarshal([]byte(test.input), &v)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), []byte(v)) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBytes(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Bytes
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBytes(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
in := test.input.([]byte)
|
||||
out, err := json.Marshal(Bytes(in))
|
||||
if err != nil {
|
||||
t.Errorf("%x: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%x: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := Bytes(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalBigTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errJSONEOF},
|
||||
{input: "null", wantErr: errNonString(bigT)},
|
||||
{input: "10", wantErr: errNonString(bigT)},
|
||||
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bigT)},
|
||||
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, bigT)},
|
||||
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
|
||||
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
|
||||
{
|
||||
input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
|
||||
wantErr: wrapTypeError(ErrBig256Range, bigT),
|
||||
},
|
||||
|
||||
// valid encoding
|
||||
{input: `""`, want: big.NewInt(0)},
|
||||
{input: `"0x0"`, want: big.NewInt(0)},
|
||||
{input: `"0x01"`, want: big.NewInt(0x01)},
|
||||
{input: `"0x001"`, want: big.NewInt(0x001)},
|
||||
{input: `"0x0001"`, want: big.NewInt(0x0001)},
|
||||
{input: `"0x2"`, want: big.NewInt(0x2)},
|
||||
{input: `"0x2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0X2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `"0xbBb"`, want: big.NewInt(0xbbb)},
|
||||
{input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `"0x112233445566778899aabbccddeeff"`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBig(t *testing.T) {
|
||||
for _, test := range unmarshalBigTests {
|
||||
var v Big
|
||||
err := json.Unmarshal([]byte(test.input), &v)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if test.want != nil && test.want.(*big.Int).Cmp((*big.Int)(&v)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, (*big.Int)(&v), test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBig(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Big
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
in := test.input.(*big.Int)
|
||||
out, err := json.Marshal((*Big)(in))
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := (*Big)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalUint64Tests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errJSONEOF},
|
||||
{input: "null", wantErr: errNonString(uint64T)},
|
||||
{input: "10", wantErr: errNonString(uint64T)},
|
||||
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uint64T)},
|
||||
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uint64T)},
|
||||
{input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUint64Range, uint64T)},
|
||||
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
|
||||
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
|
||||
|
||||
// valid encoding
|
||||
{input: `""`, want: uint64(0)},
|
||||
{input: `"0x0"`, want: uint64(0)},
|
||||
{input: `"0x01"`, want: uint64(0x01)},
|
||||
{input: `"0x001"`, want: uint64(0x001)},
|
||||
{input: `"0x0001"`, want: uint64(0x0001)},
|
||||
{input: `"0x2"`, want: uint64(0x2)},
|
||||
{input: `"0x2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0X2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: uint64(0x1122aaff)},
|
||||
{input: `"0xbbb"`, want: uint64(0xbbb)},
|
||||
{input: `"0xffffffffffffffff"`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
|
||||
func TestUnmarshalUint64(t *testing.T) {
|
||||
for _, test := range unmarshalUint64Tests {
|
||||
var v Uint64
|
||||
err := json.Unmarshal([]byte(test.input), &v)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if uint64(v) != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalUint64(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdf"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Uint64
|
||||
v.UnmarshalJSON(input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
in := test.input.(uint64)
|
||||
out, err := json.Marshal(Uint64(in))
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := (Uint64)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUint(t *testing.T) {
|
||||
for _, test := range encodeUintTests {
|
||||
in := test.input.(uint)
|
||||
out, err := json.Marshal(Uint(in))
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := (Uint)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// These are variables (not constants) to avoid constant overflow
|
||||
// checks in the compiler on 32bit platforms.
|
||||
maxUint33bits = uint64(^uint32(0)) + 1
|
||||
maxUint64bits = ^uint64(0)
|
||||
)
|
||||
|
||||
var unmarshalUintTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errJSONEOF},
|
||||
{input: "null", wantErr: errNonString(uintT)},
|
||||
{input: "10", wantErr: errNonString(uintT)},
|
||||
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uintT)},
|
||||
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uintT)},
|
||||
{input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
|
||||
{input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUintRange, uintT)},
|
||||
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
|
||||
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
|
||||
|
||||
// valid encoding
|
||||
{input: `""`, want: uint(0)},
|
||||
{input: `"0x0"`, want: uint(0)},
|
||||
{input: `"0x01"`, want: uint(0x01)},
|
||||
{input: `"0x001"`, want: uint(0x001)},
|
||||
{input: `"0x0001"`, want: uint(0x0001)},
|
||||
{input: `"0x2"`, want: uint(0x2)},
|
||||
{input: `"0x2F2"`, want: uint(0x2f2)},
|
||||
{input: `"0X2F2"`, want: uint(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: uint(0x1122aaff)},
|
||||
{input: `"0xbbb"`, want: uint(0xbbb)},
|
||||
{input: `"0xffffffff"`, want: uint(0xffffffff)},
|
||||
{input: `"0xffffffffffffffff"`, want: uint(maxUint64bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
|
||||
}
|
||||
|
||||
func TestUnmarshalUint(t *testing.T) {
|
||||
for _, test := range unmarshalUintTests {
|
||||
var v Uint
|
||||
err := json.Unmarshal([]byte(test.input), &v)
|
||||
if uintBits == 32 && test.wantErr32bit != nil {
|
||||
checkError(t, test.input, err, test.wantErr32bit)
|
||||
continue
|
||||
}
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if uint(v) != test.want.(uint) {
|
||||
t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalFixedUnprefixedText(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want []byte
|
||||
wantErr error
|
||||
}{
|
||||
{input: "0x2", wantErr: ErrOddLength},
|
||||
{input: "2", wantErr: ErrOddLength},
|
||||
{input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
|
||||
{input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
|
||||
// check that output is not modified for partially correct input
|
||||
{input: "444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
|
||||
{input: "0x444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
|
||||
// valid inputs
|
||||
{input: "44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
|
||||
{input: "0x44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
out := make([]byte, 4)
|
||||
err := UnmarshalFixedUnprefixedText("x", []byte(test.input), out)
|
||||
switch {
|
||||
case err == nil && test.wantErr != nil:
|
||||
t.Errorf("%q: got no error, expected %q", test.input, test.wantErr)
|
||||
case err != nil && test.wantErr == nil:
|
||||
t.Errorf("%q: unexpected error %q", test.input, err)
|
||||
case err != nil && err.Error() != test.wantErr.Error():
|
||||
t.Errorf("%q: error mismatch: got %q, want %q", test.input, err, test.wantErr)
|
||||
}
|
||||
if test.want != nil && !bytes.Equal(out, test.want) {
|
||||
t.Errorf("%q: output mismatch: got %x, want %x", test.input, out, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
85
qkc/common/token_codec.go
Normal file
85
qkc/common/token_codec.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common (byte-compatible).
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
TOKENBASE = uint64(36)
|
||||
TOKENIDMAX = uint64(4873763662273663091) // ZZZZZZZZZZZZ
|
||||
TOKENMAX = "ZZZZZZZZZZZZ"
|
||||
)
|
||||
|
||||
func TokenIDEncode(str string) uint64 {
|
||||
if len(str) >= 13 {
|
||||
panic(errors.New("name too long"))
|
||||
}
|
||||
// TODO check name can only contain 0-9, A-Z
|
||||
|
||||
id := TokenCharEncode(str[len(str)-1])
|
||||
base := TOKENBASE
|
||||
|
||||
len := len(str)
|
||||
for index := len - 2; index >= 0; index-- {
|
||||
id += base * (TokenCharEncode(str[index]) + 1)
|
||||
base *= TOKENBASE
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func TokenIdDecode(id uint64) (string, error) {
|
||||
if id > TOKENIDMAX {
|
||||
return "", errors.New("it too big or negative")
|
||||
}
|
||||
name := make([]byte, 0)
|
||||
t, err := TokenCharDecode(id % TOKENBASE)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name = append(name, t)
|
||||
if id/TOKENBASE < 1 {
|
||||
return string(name), nil
|
||||
}
|
||||
id = id/TOKENBASE - 1
|
||||
for id >= 0 {
|
||||
t, err := TokenCharDecode(id % TOKENBASE)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name = append(name, t)
|
||||
if id/TOKENBASE < 1 {
|
||||
break
|
||||
}
|
||||
id = id/TOKENBASE - 1
|
||||
}
|
||||
return ReverseString(string(name)), nil
|
||||
}
|
||||
func TokenCharEncode(char byte) uint64 {
|
||||
if char >= byte('A') && char <= byte('Z') {
|
||||
return 10 + uint64(char-byte('A'))
|
||||
}
|
||||
if char >= byte('0') && char <= byte('9') {
|
||||
return uint64(char - byte('0'))
|
||||
}
|
||||
panic(fmt.Errorf("unknown character %v", byte(char)))
|
||||
}
|
||||
|
||||
func TokenCharDecode(id uint64) (byte, error) {
|
||||
if !(id < TOKENBASE && id >= 0) {
|
||||
return byte(0), fmt.Errorf("incalid char %v", id)
|
||||
}
|
||||
if id < 10 {
|
||||
return byte('0' + id), nil
|
||||
}
|
||||
return byte('A' + id - 10), nil
|
||||
}
|
||||
|
||||
func ReverseString(s string) (result string) {
|
||||
for _, v := range s {
|
||||
result = string(v) + result
|
||||
}
|
||||
return
|
||||
}
|
||||
45
qkc/common/token_codec_test.go
Normal file
45
qkc/common/token_codec_test.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common (byte-compatible).
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTokenCharEncode(t *testing.T) {
|
||||
EncodedValues := make(map[string]uint64)
|
||||
EncodedValues["0"] = 0
|
||||
EncodedValues["Z"] = 35
|
||||
EncodedValues["00"] = 36
|
||||
EncodedValues["0Z"] = 71
|
||||
EncodedValues["1Z"] = 107
|
||||
EncodedValues["20"] = 108
|
||||
EncodedValues["ZZ"] = 1331
|
||||
EncodedValues["QKC"] = 35760
|
||||
EncodedValues[TOKENMAX] = TOKENIDMAX
|
||||
|
||||
for key, value := range EncodedValues {
|
||||
if value != TokenIDEncode(key) {
|
||||
t.Fatalf("key:%v should: %v is %v", key, value, TokenIDEncode(key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomToken(t *testing.T) {
|
||||
count := 100000
|
||||
for index := 0; index < count; index++ {
|
||||
data := rand.Intn(int(TOKENIDMAX))
|
||||
|
||||
deData, err := TokenIdDecode(uint64(data))
|
||||
if err != nil {
|
||||
fmt.Println("data", data)
|
||||
panic(err)
|
||||
}
|
||||
newData := TokenIDEncode(deData)
|
||||
if newData != uint64(data) {
|
||||
t.Fatalf("data:%v newData:%v", data, newData)
|
||||
}
|
||||
}
|
||||
}
|
||||
190
qkc/common/utils.go
Normal file
190
qkc/common/utils.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/common (byte-compatible).
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
"net"
|
||||
"reflect"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
const (
|
||||
DirectionToGenesis = uint8(0)
|
||||
DirectionToTip = uint8(1)
|
||||
|
||||
SkipHash = uint8(0)
|
||||
SkipHeight = uint8(1)
|
||||
)
|
||||
|
||||
var (
|
||||
EmptyHash = ethCommon.Hash{}
|
||||
uint128Max = GetUint128Max()
|
||||
)
|
||||
|
||||
func GetUint128Max() *big.Int {
|
||||
pow2_64 := new(big.Int).Add(new(big.Int).SetUint64(math.MaxUint64), ethCommon.Big1)
|
||||
pow2_128 := new(big.Int).Mul(pow2_64, pow2_64)
|
||||
return new(big.Int).Sub(pow2_128, ethCommon.Big1)
|
||||
}
|
||||
|
||||
func BiggerThanUint128Max(data *big.Int) bool {
|
||||
return data.Cmp(uint128Max) > 0
|
||||
}
|
||||
|
||||
/*
|
||||
0b101, 0b11 -> True
|
||||
0b101, 0b10 -> False
|
||||
*/
|
||||
func MasksHaveOverlap(m1, m2 uint32) bool {
|
||||
i1 := IntLeftMostBit(m1)
|
||||
i2 := IntLeftMostBit(m2)
|
||||
if i1 > i2 {
|
||||
i1 = i2
|
||||
}
|
||||
bitMask := uint32((1 << (i1 - 1)) - 1)
|
||||
return (m1 & bitMask) == (m2 & bitMask)
|
||||
}
|
||||
|
||||
// IsP2 is check num is 2^x
|
||||
func IsP2(shardSize uint32) bool {
|
||||
return (shardSize & (shardSize - 1)) == 0
|
||||
}
|
||||
|
||||
// IntLeftMostBit left most bit
|
||||
func IntLeftMostBit(v uint32) uint32 {
|
||||
return uint32(32 - bits.LeadingZeros32(v))
|
||||
}
|
||||
|
||||
func DeepCopy(dst, src interface{}) error {
|
||||
var buf bytes.Buffer
|
||||
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
|
||||
return err
|
||||
}
|
||||
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
|
||||
}
|
||||
|
||||
func GetIPV4Addr() (string, error) {
|
||||
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return "127.0.0.1", err
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ipNet, isIpNet := addr.(*net.IPNet)
|
||||
if isIpNet && !ipNet.IP.IsLoopback() {
|
||||
ipv4 := ipNet.IP.To4()
|
||||
if ipv4 != nil {
|
||||
return ipv4.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Error("ipv4 addr not found", "addr", addrs)
|
||||
return "127.0.0.1", nil
|
||||
}
|
||||
|
||||
func IsLocalIP(ip string) bool {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for i := range addrs {
|
||||
intf, _, err := net.ParseCIDR(addrs[i].String())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if net.ParseIP(ip).Equal(intf) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsNil(data interface{}) bool {
|
||||
return data == nil || reflect.ValueOf(data).IsNil()
|
||||
}
|
||||
|
||||
// ConstMinorBlockRewardCalculator blockReward struct
|
||||
type ConstMinorBlockRewardCalculator struct {
|
||||
}
|
||||
|
||||
// GetBlockReward getBlockReward
|
||||
func (c *ConstMinorBlockRewardCalculator) GetBlockReward() *big.Int {
|
||||
data := new(big.Int).SetInt64(100)
|
||||
return new(big.Int).Mul(data, new(big.Int).SetInt64(1000000000000000000))
|
||||
}
|
||||
|
||||
func BigIntMulBigRat(bigInt *big.Int, bigRat *big.Rat) *big.Int {
|
||||
bigRat1 := new(big.Rat).Set(bigRat)
|
||||
ans := new(big.Int).Mul(bigInt, bigRat1.Num())
|
||||
ans.Div(ans, bigRat1.Denom())
|
||||
return ans
|
||||
}
|
||||
|
||||
// Uint32ToBytes trans uint32 num to bytes
|
||||
func Uint32ToBytes(n uint32) []byte {
|
||||
Bytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(Bytes, n)
|
||||
return Bytes
|
||||
}
|
||||
|
||||
func Uint64ToBytes(n uint64) []byte {
|
||||
Bytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(Bytes, n)
|
||||
return Bytes
|
||||
}
|
||||
|
||||
func BytesToUint32(byte []byte) uint32 {
|
||||
bytesBuffer := bytes.NewBuffer(byte)
|
||||
var x uint32
|
||||
binary.Read(bytesBuffer, binary.BigEndian, &x)
|
||||
return x
|
||||
}
|
||||
|
||||
func EncodeToByte32(data uint64) []byte {
|
||||
ret := make([]byte, 32)
|
||||
binary.BigEndian.PutUint64(ret[24:], data)
|
||||
return ret
|
||||
}
|
||||
|
||||
func BigToByte32(data *big.Int) []byte {
|
||||
dataBytes := data.Bytes()
|
||||
lenData := len(dataBytes)
|
||||
if lenData > 32 {
|
||||
panic("data's len should <= 32")
|
||||
}
|
||||
ret := make([]byte, 32)
|
||||
copy(ret[(32-lenData):], dataBytes)
|
||||
return ret
|
||||
}
|
||||
|
||||
func Has0xPrefix(input string) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func RemoveDuplicate(data []uint64) []uint64 {
|
||||
newData := make([]uint64, 0, len(data))
|
||||
for _, iData := range data {
|
||||
if len(newData) == 0 {
|
||||
newData = append(newData, iData)
|
||||
} else {
|
||||
for k, v := range newData {
|
||||
if v == iData {
|
||||
break
|
||||
}
|
||||
if k == len(newData)-1 {
|
||||
newData = append(newData, iData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return newData
|
||||
}
|
||||
76
qkc/config/chain_config.go
Normal file
76
qkc/config/chain_config.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/cluster/config (byte-compatible).
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
ethcom "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/qkc/account"
|
||||
)
|
||||
|
||||
type ChainConfig struct {
|
||||
ChainID uint32 `json:"CHAIN_ID"`
|
||||
ShardSize uint32 `json:"SHARD_SIZE"`
|
||||
DefaultChainToken string `json:"DEFAULT_CHAIN_TOKEN"`
|
||||
ConsensusType string `json:"CONSENSUS_TYPE"`
|
||||
|
||||
// Only set when CONSENSUS_TYPE is not NONE
|
||||
ConsensusConfig *POWConfig `json:"CONSENSUS_CONFIG"`
|
||||
Genesis *ShardGenesis `json:"GENESIS"`
|
||||
|
||||
CoinbaseAddress account.Address `json:"-"`
|
||||
CoinbaseAmount *big.Int `json:"COINBASE_AMOUNT"`
|
||||
EpochInterval uint64 `json:"EPOCH_INTERVAL"`
|
||||
|
||||
DifficultyAdjustmentCutoffTime uint32 `json:"DIFFICULTY_ADJUSTMENT_CUTOFF_TIME"`
|
||||
DifficultyAdjustmentFactor uint32 `json:"DIFFICULTY_ADJUSTMENT_FACTOR"`
|
||||
ExtraShardBlocksInRootBlock uint32 `json:"EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK"`
|
||||
PoswConfig *POSWConfig `json:"POSW_CONFIG"`
|
||||
}
|
||||
|
||||
func NewChainConfig() *ChainConfig {
|
||||
return &ChainConfig{
|
||||
ChainID: 0,
|
||||
ShardSize: 2,
|
||||
DefaultChainToken: DefaultToken,
|
||||
ConsensusType: PoWNone,
|
||||
ConsensusConfig: nil,
|
||||
Genesis: NewShardGenesis(),
|
||||
CoinbaseAmount: new(big.Int).Mul(big.NewInt(5), QuarkashToJiaozi),
|
||||
DifficultyAdjustmentCutoffTime: 7,
|
||||
DifficultyAdjustmentFactor: 512,
|
||||
ExtraShardBlocksInRootBlock: 3,
|
||||
PoswConfig: NewPOSWConfig(),
|
||||
EpochInterval: uint64(210000 * 60),
|
||||
}
|
||||
}
|
||||
|
||||
type ChainConfigAlias ChainConfig
|
||||
|
||||
func (c *ChainConfig) MarshalJSON() ([]byte, error) {
|
||||
addr := c.CoinbaseAddress.ToHex()
|
||||
jsonConfig := struct {
|
||||
ChainConfigAlias
|
||||
CoinbaseAddress string `json:"COINBASE_ADDRESS"`
|
||||
}{ChainConfigAlias: ChainConfigAlias(*c), CoinbaseAddress: addr}
|
||||
return json.Marshal(jsonConfig)
|
||||
}
|
||||
|
||||
func (c *ChainConfig) UnmarshalJSON(input []byte) error {
|
||||
var jsonConfig struct {
|
||||
ChainConfigAlias
|
||||
CoinbaseAddress string `json:"COINBASE_ADDRESS"`
|
||||
}
|
||||
if err := json.Unmarshal(input, &jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
*c = ChainConfig(jsonConfig.ChainConfigAlias)
|
||||
address, err := account.CreatAddressFromBytes(ethcom.FromHex(jsonConfig.CoinbaseAddress))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.CoinbaseAddress = address
|
||||
return nil
|
||||
}
|
||||
501
qkc/config/cluster_config.go
Normal file
501
qkc/config/cluster_config.go
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/cluster/config (byte-compatible).
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
ethcom "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/qkc/account"
|
||||
"github.com/ethereum/go-ethereum/qkc/common"
|
||||
)
|
||||
|
||||
var (
|
||||
slavePort uint16 = 38000
|
||||
)
|
||||
|
||||
type ClusterConfig struct {
|
||||
P2PPort uint16 `json:"P2P_PORT"`
|
||||
JSONRPCPort uint16 `json:"JSON_RPC_PORT"`
|
||||
JSONRPCHOST string `json:"JSON_RPC_HOST"`
|
||||
PrivateJSONRPCPort uint16 `json:"PRIVATE_JSON_RPC_PORT"`
|
||||
PrivateJSONRPCHOST string `json:"PRIVATE_JSON_RPC_HOST"`
|
||||
EnableTransactionHistory bool `json:"ENABLE_TRANSACTION_HISTORY"`
|
||||
DbPathRoot string `json:"DB_PATH_ROOT"`
|
||||
LogLevel string `json:"LOG_LEVEL"`
|
||||
StartSimulatedMining bool `json:"START_SIMULATED_MINING"`
|
||||
Clean bool `json:"CLEAN"`
|
||||
GenesisDir string `json:"GENESIS_DIR"`
|
||||
Quarkchain *QuarkChainConfig `json:"QUARKCHAIN"`
|
||||
Master *MasterConfig `json:"MASTER"`
|
||||
SlaveList []*SlaveConfig `json:"SLAVE_LIST"`
|
||||
SimpleNetwork *SimpleNetwork `json:"SIMPLE_NETWORK,omitempty"`
|
||||
P2P *P2PConfig `json:"P2P,omitempty"`
|
||||
Monitoring *MonitoringConfig `json:"MONITORING"`
|
||||
CheckDB bool
|
||||
CheckDBRBlockFrom int
|
||||
CheckDBRBlockTo int
|
||||
CheckDBRBlockBatch int
|
||||
NoPruning bool
|
||||
}
|
||||
|
||||
func NewClusterConfig() *ClusterConfig {
|
||||
var ret = ClusterConfig{
|
||||
P2PPort: DefaultP2PPort,
|
||||
JSONRPCPort: DefaultPubRpcPort,
|
||||
JSONRPCHOST: "0.0.0.0",
|
||||
PrivateJSONRPCPort: DefaultPrivRpcPort,
|
||||
PrivateJSONRPCHOST: DefaultHost,
|
||||
EnableTransactionHistory: false,
|
||||
DbPathRoot: "./db",
|
||||
LogLevel: "info",
|
||||
StartSimulatedMining: false,
|
||||
Clean: false,
|
||||
GenesisDir: "../genesis_data",
|
||||
Quarkchain: NewQuarkChainConfig(),
|
||||
Master: NewMasterConfig(),
|
||||
SimpleNetwork: NewSimpleNetwork(),
|
||||
P2P: NewP2PConfig(),
|
||||
Monitoring: NewMonitoringConfig(),
|
||||
CheckDB: false,
|
||||
CheckDBRBlockFrom: -1,
|
||||
CheckDBRBlockTo: 0,
|
||||
CheckDBRBlockBatch: 10,
|
||||
}
|
||||
|
||||
fullShardIds := ret.Quarkchain.GetGenesisShardIds()
|
||||
|
||||
for i := 0; i < DefaultNumSlaves; i++ {
|
||||
slave := NewDefaultSlaveConfig()
|
||||
slave.Port = slavePort + uint16(i)
|
||||
slave.ID = fmt.Sprintf("S%d", i)
|
||||
slave.FullShardList = append(slave.FullShardList, getFullShardIdListFromSlaveIndex(fullShardIds, DefaultNumSlaves, i)...)
|
||||
ret.SlaveList = append(ret.SlaveList, slave)
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func getFullShardIdListFromSlaveIndex(list []uint32, slaveNumber int, slaveIndex int) []uint32 {
|
||||
ans := make([]uint32, 0)
|
||||
for index := 0; index < len(list); index++ {
|
||||
if index%slaveNumber == slaveIndex {
|
||||
ans = append(ans, list[index])
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) GetSlaveConfig(id string) (*SlaveConfig, error) {
|
||||
if c.SlaveList == nil {
|
||||
return nil, errors.New("slave config is empty")
|
||||
}
|
||||
for _, slave := range c.SlaveList {
|
||||
if slave != nil && slave.ID == id {
|
||||
return slave, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("slave %s is not in cluster config", id)
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) BackWardChainMaskList() error {
|
||||
setFullShardFromChainMask := true
|
||||
for _, slave := range c.SlaveList {
|
||||
for _, vv := range slave.FullShardList {
|
||||
if vv != 0 {
|
||||
setFullShardFromChainMask = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if !setFullShardFromChainMask {
|
||||
return nil
|
||||
}
|
||||
for _, v := range c.Quarkchain.Chains {
|
||||
if v.ShardSize != 1 {
|
||||
return errors.New("CHAIN_MASK_LIST:only works if every chain has 1 shard only")
|
||||
}
|
||||
}
|
||||
for _, v := range c.SlaveList {
|
||||
for _, m := range v.ChainMaskListForBackward {
|
||||
bitMask := uint32(1<<(common.IntLeftMostBit(m)-1) - 1)
|
||||
v.FullShardList = make([]uint32, 0)
|
||||
for _, chainConfig := range c.Quarkchain.Chains {
|
||||
if chainConfig.ChainID&bitMask == m&bitMask {
|
||||
v.FullShardList = append(v.FullShardList, chainConfig.ChainID<<16+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(fullShardList(v.FullShardList))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type fullShardList []uint32
|
||||
|
||||
func (a fullShardList) Len() int { return len(a) }
|
||||
func (a fullShardList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a fullShardList) Less(i, j int) bool { return a[i] < a[j] }
|
||||
|
||||
type QuarkChainConfig struct {
|
||||
ChainSize uint32 `json:"CHAIN_SIZE"`
|
||||
MaxNeighbors uint32 `json:"MAX_NEIGHBORS"`
|
||||
NetworkID uint32 `json:"NETWORK_ID"`
|
||||
TransactionQueueSizeLimitPerShard uint64 `json:"TRANSACTION_QUEUE_SIZE_LIMIT_PER_SHARD"`
|
||||
BlockExtraDataSizeLimit uint32 `json:"BLOCK_EXTRA_DATA_SIZE_LIMIT"`
|
||||
GuardianPublicKey []byte `json:"-"`
|
||||
RootSignerPrivateKey []byte `json:"-"`
|
||||
P2PProtocolVersion uint32 `json:"P2P_PROTOCOL_VERSION"`
|
||||
P2PCommandSizeLimit uint32 `json:"P2P_COMMAND_SIZE_LIMIT"`
|
||||
SkipRootDifficultyCheck bool `json:"SKIP_ROOT_DIFFICULTY_CHECK"`
|
||||
SkipRootCoinbaseCheck bool `json:"SKIP_ROOT_COINBASE_CHECK"`
|
||||
SkipMinorDifficultyCheck bool `json:"SKIP_MINOR_DIFFICULTY_CHECK"`
|
||||
GenesisToken string `json:"GENESIS_TOKEN"`
|
||||
Root *RootConfig `json:"ROOT"`
|
||||
shards map[uint32]*ShardConfig
|
||||
Chains map[uint32]*ChainConfig `json:"-"`
|
||||
RewardTaxRate *big.Rat `json:"-"`
|
||||
LocalFeeRate *big.Rat `json:"-"`
|
||||
RewardCalculateRate *big.Rat `json:"-"`
|
||||
BlockRewardDecayFactor *big.Rat `json:"-"`
|
||||
chainIdToShardSize map[uint32]uint32
|
||||
chainIdToShardIds map[uint32][]uint32
|
||||
defaultChainTokenID uint64
|
||||
EnableEvmTimeStamp uint64 `json:"ENABLE_EVM_TIMESTAMP"`
|
||||
EnableQkcHashXHeight uint64 `json:"ENABLE_QKCHASHX_HEIGHT"`
|
||||
EnableNonReservedNativeTokenTimestamp uint64 `json:"ENABLE_NON_RESERVED_NATIVE_TOKEN_TIMESTAMP"`
|
||||
EnableGeneralNativeTokenTimestamp uint64 `json:"ENABLE_GENERAL_NATIVE_TOKEN_TIMESTAMP"`
|
||||
EnablePoswStakingDecayTimestamp uint64 `json:"ENABLE_POSW_STAKING_DECAY_TIMESTAMP"`
|
||||
EnableEIP155SignerTimestamp uint64 `json:"ENABLE_EIP155_SIGNER_TIMESTAMP"`
|
||||
BaseEthChainID uint32 `json:"BASE_ETH_CHAIN_ID"`
|
||||
DisablePowCheck bool `json:"DISABLE_POW_CHECK"`
|
||||
XShardGasDDOSFixRootHeight uint64 `json:"XSHARD_GAS_DDOS_FIX_ROOT_HEIGHT"`
|
||||
MinTXPoolGasPrice *big.Int `json:"MIN_TX_POOL_GAS_PRICE"`
|
||||
MinMiningGasPrice *big.Int `json:"MIN_MINING_GAS_PRICE"`
|
||||
RootChainPoSWContractBytecodeHash ethcom.Hash `json:"-"`
|
||||
}
|
||||
|
||||
type QuarkChainConfigAlias QuarkChainConfig
|
||||
type jsonConfig struct {
|
||||
QuarkChainConfigAlias
|
||||
GuardianPublicKey string `json:"GUARDIAN_PUBLIC_KEY"`
|
||||
RootSignerPrivateKey string `json:"ROOT_SIGNER_PRIVATE_KEY"`
|
||||
Chains []*ChainConfig `json:"CHAINS"`
|
||||
RewardTaxRate float64 `json:"REWARD_TAX_RATE"`
|
||||
BlockRewardDecayFactor float64 `json:"BLOCK_REWARD_DECAY_FACTOR"`
|
||||
RootChainPoSWContractBytecodeHash string `json:"ROOT_CHAIN_POSW_CONTRACT_BYTECODE_HASH"`
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) MarshalJSON() ([]byte, error) {
|
||||
rewardTaxRate, _ := q.RewardTaxRate.Float64()
|
||||
BlockRewardDecayFactor, _ := q.BlockRewardDecayFactor.Float64()
|
||||
chains := make([]*ChainConfig, 0, len(q.Chains))
|
||||
rootChainPoSWContractBytecodeHash := ethcom.Bytes2Hex(q.RootChainPoSWContractBytecodeHash[:])
|
||||
for _, chain := range q.Chains {
|
||||
chains = append(chains, chain)
|
||||
}
|
||||
jConfig := jsonConfig{
|
||||
QuarkChainConfigAlias(*q),
|
||||
hex.EncodeToString(q.GuardianPublicKey),
|
||||
hex.EncodeToString(q.RootSignerPrivateKey),
|
||||
chains,
|
||||
rewardTaxRate,
|
||||
BlockRewardDecayFactor,
|
||||
rootChainPoSWContractBytecodeHash,
|
||||
}
|
||||
return json.Marshal(jConfig)
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) UnmarshalJSON(input []byte) error {
|
||||
jConfig := &jsonConfig{}
|
||||
if err := json.Unmarshal(input, jConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Slice(jConfig.Chains, func(i, j int) bool { return jConfig.Chains[i].ChainID < jConfig.Chains[j].ChainID })
|
||||
|
||||
*q = QuarkChainConfig(jConfig.QuarkChainConfigAlias)
|
||||
q.Chains = make(map[uint32]*ChainConfig)
|
||||
q.shards = make(map[uint32]*ShardConfig)
|
||||
for _, chainCfg := range jConfig.Chains {
|
||||
q.Chains[chainCfg.ChainID] = chainCfg
|
||||
for shardID := uint32(0); shardID < chainCfg.ShardSize; shardID++ {
|
||||
var cfg = new(ChainConfig)
|
||||
_ = common.DeepCopy(cfg, chainCfg)
|
||||
shardCfg := NewShardConfig(cfg)
|
||||
shardCfg.SetRootConfig(q.Root)
|
||||
shardCfg.ShardID = shardID
|
||||
// Follow pyquarkchain (ClusterConfig.from_dict): the per-shard config is
|
||||
// derived from the chain config by (1) rewriting the coinbase into this
|
||||
// shard and (2) filtering GENESIS.ALLOC down to the addresses that belong
|
||||
// to this shard. goquarkchain's UnmarshalJSON does neither (it copies the
|
||||
// chain coinbase and the full alloc verbatim), but the goshard slave shares
|
||||
// its config with a pyquarkchain master, so the per-shard config must be
|
||||
// derived exactly as pyquarkchain derives it.
|
||||
shardCfg.CoinbaseAddress = chainCfg.CoinbaseAddress.AddressInShard(shardCfg.GetFullShardId())
|
||||
if chainCfg.Genesis != nil && shardCfg.Genesis != nil {
|
||||
alloc := make(map[account.Address]Allocation)
|
||||
for addr, allocation := range chainCfg.Genesis.Alloc {
|
||||
if addr.FullShardKey&(chainCfg.ShardSize-1) == shardID {
|
||||
alloc[addr] = allocation
|
||||
}
|
||||
}
|
||||
shardCfg.Genesis.Alloc = alloc
|
||||
}
|
||||
q.shards[shardCfg.GetFullShardId()] = shardCfg
|
||||
}
|
||||
}
|
||||
var denom int64 = 1000
|
||||
q.RewardTaxRate = big.NewRat(int64(jConfig.RewardTaxRate*float64(denom)), denom)
|
||||
one := big.NewRat(1, 1)
|
||||
q.LocalFeeRate = one.Sub(one, q.RewardTaxRate)
|
||||
q.RewardCalculateRate = new(big.Rat).Quo(q.RewardTaxRate, q.LocalFeeRate)
|
||||
q.BlockRewardDecayFactor = big.NewRat(int64(jConfig.BlockRewardDecayFactor*float64(denom)), denom)
|
||||
q.RootChainPoSWContractBytecodeHash = ethcom.HexToHash(jConfig.RootChainPoSWContractBytecodeHash)
|
||||
|
||||
q.GuardianPublicKey = ethcom.FromHex(jConfig.GuardianPublicKey)
|
||||
q.RootSignerPrivateKey = ethcom.FromHex(jConfig.RootSignerPrivateKey)
|
||||
if len(q.GuardianPublicKey) == 64 {
|
||||
q.GuardianPublicKey = append([]byte{byte(0x4)}, q.GuardianPublicKey...)
|
||||
}
|
||||
q.initAndValidate()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the root block height at which the shard shall be created
|
||||
func (q *QuarkChainConfig) GetGenesisRootHeight(fullShardId uint32) uint32 {
|
||||
return q.shards[fullShardId].Genesis.RootHeight
|
||||
}
|
||||
|
||||
// GetGenesisShardIds returns a list of ids for shards that have GENESIS.
|
||||
func (q *QuarkChainConfig) GetGenesisShardIds() []uint32 {
|
||||
var result []uint32
|
||||
for shardID := range q.shards {
|
||||
result = append(result, shardID)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Return a list of ids of the shards that have been initialized before a certain root height
|
||||
func (q *QuarkChainConfig) GetInitializedShardIdsBeforeRootHeight(rootHeight uint32) []uint32 {
|
||||
var result []uint32
|
||||
for fullShardId, config := range q.shards {
|
||||
if config.Genesis != nil && config.Genesis.RootHeight < rootHeight {
|
||||
result = append(result, uint32(fullShardId))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) Update(chainSize, shardSizePerChain, rootBlockTime, minorBlockTime uint32) {
|
||||
q.ChainSize = chainSize
|
||||
if q.Root == nil {
|
||||
q.Root = NewRootConfig()
|
||||
}
|
||||
q.Root.ConsensusType = PoWSimulate
|
||||
if q.Root.ConsensusConfig == nil {
|
||||
q.Root.ConsensusConfig = NewPOWConfig()
|
||||
}
|
||||
q.Root.ConsensusConfig.TargetBlockTime = rootBlockTime
|
||||
|
||||
q.Chains = make(map[uint32]*ChainConfig)
|
||||
q.shards = make(map[uint32]*ShardConfig)
|
||||
for chainId := uint32(0); chainId < chainSize; chainId++ {
|
||||
chainCfg := NewChainConfig()
|
||||
chainCfg.ChainID = chainId
|
||||
chainCfg.ShardSize = shardSizePerChain
|
||||
chainCfg.ConsensusType = PoWSimulate
|
||||
chainCfg.ConsensusConfig = NewPOWConfig()
|
||||
chainCfg.ConsensusConfig.TargetBlockTime = minorBlockTime
|
||||
chainCfg.DefaultChainToken = DefaultToken
|
||||
q.Chains[chainId] = chainCfg
|
||||
for shardId := uint32(0); shardId < shardSizePerChain; shardId++ {
|
||||
var cfg = new(ChainConfig)
|
||||
_ = common.DeepCopy(cfg, chainCfg)
|
||||
shardCfg := NewShardConfig(cfg)
|
||||
shardCfg.SetRootConfig(q.Root)
|
||||
shardCfg.ShardID = shardId
|
||||
// shardCfg.CoinbaseAddress = account.CreatEmptyAddress(shardCfg.GetFullShardId())
|
||||
q.shards[shardCfg.GetFullShardId()] = shardCfg
|
||||
}
|
||||
}
|
||||
q.initAndValidate()
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) initAndValidate() {
|
||||
if q.MinMiningGasPrice == nil {
|
||||
q.MinMiningGasPrice = new(big.Int).SetUint64(1000000000)
|
||||
}
|
||||
if q.MinTXPoolGasPrice == nil {
|
||||
q.MinTXPoolGasPrice = new(big.Int).SetUint64(1000000000)
|
||||
}
|
||||
if q.XShardGasDDOSFixRootHeight == 0 {
|
||||
q.XShardGasDDOSFixRootHeight = 90000
|
||||
}
|
||||
if len(q.GuardianPublicKey) != 65 && len(q.GuardianPublicKey) != 0 {
|
||||
fmt.Println("len", len(q.GuardianPublicKey))
|
||||
panic("GuardianPublicKey should 0 or 65")
|
||||
}
|
||||
q.chainIdToShardSize = make(map[uint32]uint32)
|
||||
q.chainIdToShardIds = make(map[uint32][]uint32)
|
||||
|
||||
for fullShardId, shardCfg := range q.shards {
|
||||
chainID := shardCfg.ChainID
|
||||
shardSize := shardCfg.ShardSize
|
||||
shardID := shardCfg.ShardID
|
||||
realID := (chainID << 16) | shardSize | shardID
|
||||
|
||||
if fullShardId != realID {
|
||||
panic(fmt.Sprintf("full_shard_id is not right, target=%d, actual=%d", realID, fullShardId))
|
||||
} else {
|
||||
q.chainIdToShardSize[chainID] = shardSize
|
||||
}
|
||||
if q.chainIdToShardIds[chainID] == nil {
|
||||
q.chainIdToShardIds[chainID] = make([]uint32, 0)
|
||||
}
|
||||
q.chainIdToShardIds[chainID] = append(q.chainIdToShardIds[chainID], shardID)
|
||||
}
|
||||
chainIDMap := make(map[uint32]uint32)
|
||||
for chainID, shardIDs := range q.chainIdToShardIds {
|
||||
chainIDMap[chainID] = chainID
|
||||
shardSize, err := q.GetShardSizeByChainId(chainID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(shardIDs) != int(shardSize) {
|
||||
panic(fmt.Sprintf("shard_size length is not right, target=%d, actual=%d", shardSize, len(shardIDs)))
|
||||
}
|
||||
for i := uint32(0); i < shardSize; i++ {
|
||||
exist := false
|
||||
for _, shardID := range shardIDs {
|
||||
if i == shardID {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
panic(fmt.Sprintf("shard ids is not right, target=%d, actual=%d", i, shardIDs[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := uint32(0); i < q.ChainSize; i++ {
|
||||
if _, ok := chainIDMap[i]; !ok {
|
||||
panic(fmt.Sprintf("chain id %d is missing from the configuration", i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) GetShardConfigByFullShardID(fullShardID uint32) *ShardConfig {
|
||||
return q.shards[fullShardID]
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) IsSameFullShard(key1, key2 uint32) bool {
|
||||
id1, err := q.GetFullShardIdByFullShardKey(key1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
id2, err := q.GetFullShardIdByFullShardKey(key2)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return id1 == id2
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) GetFullShardIdByFullShardKey(fullShardKey uint32) (uint32, error) {
|
||||
chainID := fullShardKey >> 16
|
||||
shardSize, err := q.GetShardSizeByChainId(chainID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
shardID := fullShardKey & (shardSize - 1)
|
||||
return (chainID << 16) | shardSize | shardID, nil
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) GetShardSizeByChainId(ID uint32) (uint32, error) {
|
||||
data, ok := q.chainIdToShardSize[ID]
|
||||
if !ok {
|
||||
return 0, errors.New("no such chainID")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func NewQuarkChainConfig() *QuarkChainConfig {
|
||||
var ret = QuarkChainConfig{
|
||||
ChainSize: 3,
|
||||
MaxNeighbors: 32,
|
||||
NetworkID: 3,
|
||||
TransactionQueueSizeLimitPerShard: 10000,
|
||||
BlockExtraDataSizeLimit: 1024,
|
||||
GuardianPublicKey: ethcom.FromHex("04ab856abd0983a82972021e454fcf66ed5940ed595b0898bcd75cbe2d0a51a00f5358b566df22395a2a8bf6c022c1d51a2c3defe654e91a8d244947783029694d"),
|
||||
RootSignerPrivateKey: nil,
|
||||
P2PProtocolVersion: 0,
|
||||
P2PCommandSizeLimit: DefaultP2PCmddSizeLimit,
|
||||
SkipRootDifficultyCheck: false,
|
||||
SkipRootCoinbaseCheck: false,
|
||||
SkipMinorDifficultyCheck: false,
|
||||
GenesisToken: DefaultToken,
|
||||
RewardTaxRate: new(big.Rat).SetFloat64(0.5),
|
||||
BlockRewardDecayFactor: new(big.Rat).SetFloat64(0.5),
|
||||
Root: NewRootConfig(),
|
||||
MinTXPoolGasPrice: new(big.Int).SetUint64(1000000000),
|
||||
MinMiningGasPrice: new(big.Int).SetUint64(1000000000),
|
||||
XShardGasDDOSFixRootHeight: 90000,
|
||||
EnableEvmTimeStamp: 1569567600,
|
||||
EnableNonReservedNativeTokenTimestamp: math.MaxUint64,
|
||||
EnableGeneralNativeTokenTimestamp: math.MaxUint64,
|
||||
RootChainPoSWContractBytecodeHash: ethcom.HexToHash("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
}
|
||||
|
||||
ret.Root.ConsensusType = PoWSimulate
|
||||
ret.Root.ConsensusConfig = NewPOWConfig()
|
||||
ret.Root.ConsensusConfig.TargetBlockTime = 10
|
||||
|
||||
one := big.NewRat(1, 1)
|
||||
ret.LocalFeeRate = one.Sub(one, ret.RewardTaxRate)
|
||||
ret.RewardCalculateRate = new(big.Rat).Quo(ret.RewardTaxRate, ret.LocalFeeRate)
|
||||
|
||||
ret.Chains = make(map[uint32]*ChainConfig)
|
||||
ret.shards = make(map[uint32]*ShardConfig)
|
||||
for chainID := uint32(0); chainID < ret.ChainSize; chainID++ {
|
||||
cfg := NewChainConfig()
|
||||
cfg.ChainID = chainID
|
||||
cfg.ConsensusType = PoWSimulate
|
||||
cfg.ConsensusConfig = NewPOWConfig()
|
||||
cfg.ConsensusConfig.TargetBlockTime = 3
|
||||
ret.Chains[chainID] = cfg
|
||||
for shardID := uint32(0); shardID < cfg.ShardSize; shardID++ {
|
||||
var chainCfg = new(ChainConfig)
|
||||
_ = common.DeepCopy(chainCfg, cfg)
|
||||
shardCfg := NewShardConfig(chainCfg)
|
||||
shardCfg.SetRootConfig(ret.Root)
|
||||
shardCfg.ShardID = shardID
|
||||
shardCfg.CoinbaseAddress = account.CreatEmptyAddress(shardCfg.GetFullShardId())
|
||||
ret.shards[shardCfg.GetFullShardId()] = shardCfg
|
||||
}
|
||||
}
|
||||
ret.initAndValidate()
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) SetShardsAndValidate(shards map[uint32]*ShardConfig) { // only used in gen config
|
||||
q.shards = shards
|
||||
q.initAndValidate()
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) GetDefaultChainTokenID() uint64 {
|
||||
if q.defaultChainTokenID == 0 {
|
||||
q.defaultChainTokenID = common.TokenIDEncode(q.GenesisToken)
|
||||
}
|
||||
return q.defaultChainTokenID
|
||||
}
|
||||
|
||||
func (q *QuarkChainConfig) GasLimit(fullShardID uint32) (*big.Int, error) {
|
||||
data, ok := q.shards[fullShardID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no such fullShardID %v", fullShardID)
|
||||
}
|
||||
return new(big.Int).SetUint64(data.Genesis.GasLimit), nil
|
||||
}
|
||||
418
qkc/config/config.go
Normal file
418
qkc/config/config.go
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/cluster/config (byte-compatible).
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/qkc/account"
|
||||
"github.com/ethereum/go-ethereum/qkc/params"
|
||||
)
|
||||
|
||||
const (
|
||||
// PoWNone is the default empty consensus type specifying no shard.
|
||||
PoWNone = "NONE"
|
||||
// PoWEthash is the consensus type running ethash algorithm.
|
||||
PoWEthash = "POW_ETHASH"
|
||||
// PoWDoubleSha256 is the consensus type running double-sha256 algorithm.
|
||||
PoWDoubleSha256 = "POW_DOUBLESHA256"
|
||||
// PoWSimulate is the simulated consensus type by simply sleeping.
|
||||
PoWSimulate = "POW_SIMULATE"
|
||||
// PoWQkchash is the consensus type running qkchash algorithm.
|
||||
PoWQkchash = "POW_QKCHASH"
|
||||
|
||||
DefaultP2PPort uint16 = 38291
|
||||
DefaultPubRpcPort uint16 = 38391
|
||||
DefaultPrivRpcPort uint16 = 38491
|
||||
DefaultHost = "localhost"
|
||||
|
||||
HeartbeatInterval = time.Duration(4 * time.Second)
|
||||
)
|
||||
|
||||
var (
|
||||
QuarkashToJiaozi = big.NewInt(1000000000000000000)
|
||||
DefaultNumSlaves = 4
|
||||
DefaultToken = "QKC"
|
||||
DefaultP2PCmddSizeLimit uint32 = 128 * 1024 * 1024
|
||||
)
|
||||
|
||||
var (
|
||||
templateFile = "alloc/%d.json"
|
||||
testFile = "loadtest.json"
|
||||
tempErrMsg = "Error importing genesis accounts from %s: %v "
|
||||
testErrMsg = "No loadtest accounts imported into genesis alloc %s: %v "
|
||||
)
|
||||
|
||||
type POWConfig struct {
|
||||
TargetBlockTime uint32 `json:"TARGET_BLOCK_TIME"`
|
||||
RemoteMine bool `json:"REMOTE_MINE"`
|
||||
}
|
||||
|
||||
func NewPOWConfig() *POWConfig {
|
||||
return &POWConfig{
|
||||
TargetBlockTime: 10,
|
||||
RemoteMine: false,
|
||||
}
|
||||
}
|
||||
|
||||
type POSWConfig struct {
|
||||
Enabled bool `json:"ENABLED"`
|
||||
EnableTimestamp uint64 `json:"ENABLE_TIMESTAMP"`
|
||||
DiffDivider uint64 `json:"DIFF_DIVIDER"`
|
||||
WindowSize uint64 `json:"WINDOW_SIZE"`
|
||||
TotalStakePerBlock *big.Int `json:"TOTAL_STAKE_PER_BLOCK"`
|
||||
BoostTimestamp uint64 `json:"BOOST_TIMESTAMP"`
|
||||
BoostMultiplierPerStep uint64 `json:"BOOST_MULTIPLIER_PER_STEP"`
|
||||
BoostSteps uint64 `json:"BOOST_STEPS"`
|
||||
BoostStepInterval uint64 `json:"BOOST_STEP_INTERVAL"`
|
||||
}
|
||||
|
||||
func NewPOSWConfig() *POSWConfig {
|
||||
return &POSWConfig{
|
||||
Enabled: false,
|
||||
EnableTimestamp: 0,
|
||||
DiffDivider: 20,
|
||||
WindowSize: 256,
|
||||
TotalStakePerBlock: new(big.Int).Mul(big.NewInt(1000000000), QuarkashToJiaozi),
|
||||
BoostTimestamp: 0, // 0 = disable
|
||||
BoostMultiplierPerStep: 2, // increase 2 times every time
|
||||
BoostSteps: 10, // max 2 ^ 10 * DiffDivider times
|
||||
BoostStepInterval: 43200, // 12 hours
|
||||
}
|
||||
}
|
||||
|
||||
func NewRootPOSWConfig() *POSWConfig {
|
||||
return &POSWConfig{
|
||||
Enabled: false,
|
||||
EnableTimestamp: 0,
|
||||
DiffDivider: 1000,
|
||||
WindowSize: 4320, // 72 hours
|
||||
TotalStakePerBlock: new(big.Int).Mul(big.NewInt(240000), QuarkashToJiaozi),
|
||||
BoostTimestamp: 0, // 0 = disable
|
||||
BoostMultiplierPerStep: 2, // increase 2 times every time
|
||||
BoostSteps: 10, // max 2 ^ 10 * DiffDivider times
|
||||
BoostStepInterval: 86400 * 2, // two days
|
||||
}
|
||||
}
|
||||
|
||||
func (c *POSWConfig) GetDiffDivider(blocktime uint64) uint64 {
|
||||
diffDivider := c.DiffDivider
|
||||
if c.BoostTimestamp > 0 && blocktime >= c.BoostTimestamp {
|
||||
steps := (blocktime-c.BoostTimestamp)/c.BoostStepInterval + 1
|
||||
if steps > c.BoostSteps {
|
||||
steps = c.BoostSteps
|
||||
}
|
||||
|
||||
diffDivider = c.DiffDivider * pow(c.BoostMultiplierPerStep, steps)
|
||||
}
|
||||
|
||||
return diffDivider
|
||||
}
|
||||
|
||||
func pow(value, n uint64) uint64 {
|
||||
r := uint64(1)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
r *= value
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type SimpleNetwork struct {
|
||||
BootstrapHost string `json:"BOOT_STRAP_HOST"`
|
||||
BootstrapPort uint16 `json:"BOOT_STRAP_PORT"`
|
||||
}
|
||||
|
||||
func NewSimpleNetwork() *SimpleNetwork {
|
||||
return &SimpleNetwork{
|
||||
BootstrapHost: "127.0.0.1",
|
||||
BootstrapPort: DefaultP2PPort,
|
||||
}
|
||||
}
|
||||
|
||||
type RootGenesis struct {
|
||||
Version uint32 `json:"VERSION"`
|
||||
Height uint32 `json:"HEIGHT"`
|
||||
HashPrevBlock string `json:"HASH_PREV_BLOCK"`
|
||||
HashMerkleRoot string `json:"HASH_MERKLE_ROOT"`
|
||||
Timestamp uint64 `json:"TIMESTAMP"`
|
||||
Difficulty uint64 `json:"DIFFICULTY"`
|
||||
Nonce uint32 `json:"NONCE"`
|
||||
}
|
||||
|
||||
func NewRootGenesis() *RootGenesis {
|
||||
return &RootGenesis{
|
||||
Version: 0,
|
||||
Height: 0,
|
||||
HashPrevBlock: "",
|
||||
HashMerkleRoot: "",
|
||||
Timestamp: 1519147489,
|
||||
Difficulty: 1000000,
|
||||
Nonce: 0,
|
||||
}
|
||||
}
|
||||
|
||||
type RootConfig struct {
|
||||
// To ignore super old blocks from peers
|
||||
// This means the network will fork permanently after a long partition
|
||||
// Use Ethereum's number, which is
|
||||
// - 30000 * 3 blocks = 90000 * 15 / 3600 = 375 hours = 375 * 3600 / 60 = 22500
|
||||
MaxStaleRootBlockHeightDiff uint64 `json:"MAX_STALE_ROOT_BLOCK_HEIGHT_DIFF"`
|
||||
ConsensusType string `json:"CONSENSUS_TYPE"`
|
||||
ConsensusConfig *POWConfig `json:"CONSENSUS_CONFIG"`
|
||||
Genesis *RootGenesis `json:"GENESIS"`
|
||||
CoinbaseAddress account.Address `json:"-"`
|
||||
CoinbaseAmount *big.Int `json:"COINBASE_AMOUNT"`
|
||||
EpochInterval uint64 `json:"EPOCH_INTERVAL"`
|
||||
DifficultyAdjustmentCutoffTime uint32 `json:"DIFFICULTY_ADJUSTMENT_CUTOFF_TIME"`
|
||||
DifficultyAdjustmentFactor uint32 `json:"DIFFICULTY_ADJUSTMENT_FACTOR"`
|
||||
PoSWConfig *POSWConfig `json:"POSW_CONFIG"`
|
||||
}
|
||||
|
||||
func NewRootConfig() *RootConfig {
|
||||
return &RootConfig{
|
||||
MaxStaleRootBlockHeightDiff: 22500,
|
||||
ConsensusType: PoWNone,
|
||||
ConsensusConfig: nil,
|
||||
Genesis: NewRootGenesis(),
|
||||
CoinbaseAddress: account.CreatEmptyAddress(0),
|
||||
CoinbaseAmount: new(big.Int).Mul(big.NewInt(120), QuarkashToJiaozi),
|
||||
EpochInterval: uint64(210000 * 10),
|
||||
DifficultyAdjustmentCutoffTime: 40,
|
||||
DifficultyAdjustmentFactor: 1024,
|
||||
PoSWConfig: NewRootPOSWConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
type RootConfigAlias RootConfig
|
||||
|
||||
func (r *RootConfig) MarshalJSON() ([]byte, error) {
|
||||
addr := r.CoinbaseAddress.ToHex()
|
||||
jsonConfig := struct {
|
||||
RootConfigAlias
|
||||
CoinbaseAddress string `json:"COINBASE_ADDRESS"`
|
||||
}{RootConfigAlias: RootConfigAlias(*r), CoinbaseAddress: addr}
|
||||
return json.Marshal(jsonConfig)
|
||||
}
|
||||
|
||||
func (r *RootConfig) UnmarshalJSON(input []byte) error {
|
||||
var jsonConfig struct {
|
||||
RootConfigAlias
|
||||
CoinbaseAddress string `json:"COINBASE_ADDRESS"`
|
||||
}
|
||||
if err := json.Unmarshal(input, &jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = RootConfig(jsonConfig.RootConfigAlias)
|
||||
address, err := account.CreatAddressFromBytes(common.FromHex(jsonConfig.CoinbaseAddress))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.CoinbaseAddress = address
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RootConfig) MaxRootBlocksInMemory() uint64 {
|
||||
return r.MaxStaleRootBlockHeightDiff * 2
|
||||
}
|
||||
|
||||
// TODO need to wait SharInfo be realized.
|
||||
/*func (c *ClusterConfig) GetSlaveInfoList() []*SlaveConfig {}*/
|
||||
|
||||
type NetWorkId struct {
|
||||
Mainnet uint32 `json:"MAINNET"`
|
||||
TestnetPorsche uint32 `json:"TESTNET_PORSCHE"` // TESTNET_FORD = 2
|
||||
}
|
||||
|
||||
type MasterConfig struct {
|
||||
// default 1.0
|
||||
MasterToSlaveConnectRetryDelay float32 `json:"MASTER_TO_SLAVE_CONNECT_RETRY_DELAY"`
|
||||
}
|
||||
|
||||
func NewMasterConfig() *MasterConfig {
|
||||
return &MasterConfig{
|
||||
MasterToSlaveConnectRetryDelay: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move to P2P
|
||||
type P2PConfig struct {
|
||||
// *new p2p module*
|
||||
BootNodes string `json:"BOOT_NODES"` // comma separated encodes format: encode://PUBKEY@IP:PORT
|
||||
PrivKey string `json:"PRIV_KEY"`
|
||||
MaxPeers uint64 `json:"MAX_PEERS"`
|
||||
UPnP bool `json:"UPNP"`
|
||||
AllowDialInRatio float32 `json:"ALLOW_DIAL_IN_RATIO"`
|
||||
PreferredNodes string `json:"PREFERRED_NODES"`
|
||||
}
|
||||
|
||||
func NewP2PConfig() *P2PConfig {
|
||||
return &P2PConfig{
|
||||
BootNodes: "",
|
||||
PrivKey: "",
|
||||
MaxPeers: 25,
|
||||
UPnP: false,
|
||||
AllowDialInRatio: 1.0,
|
||||
PreferredNodes: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *P2PConfig) GetBootNodes() []string {
|
||||
return strings.Split(s.BootNodes, ",")
|
||||
}
|
||||
|
||||
type MonitoringConfig struct {
|
||||
NetworkName string `json:"NETWORK_NAME"`
|
||||
ClusterID string `json:"CLUSTER_ID"`
|
||||
KafkaRestAddress string `json:"KAFKA_REST_ADDRESS"` // REST API endpoint for logging to Kafka, IP[:PORT] format
|
||||
MinerTopic string `json:"MINER_TOPIC"` // "qkc_miner"
|
||||
PropagationTopic string `json:"PROPAGATION_TOPIC"` // "block_propagation"
|
||||
Errors string `json:"ERRORS"` // "error"
|
||||
}
|
||||
|
||||
func NewMonitoringConfig() *MonitoringConfig {
|
||||
return &MonitoringConfig{
|
||||
NetworkName: "",
|
||||
ClusterID: "127.0.0.1",
|
||||
KafkaRestAddress: "",
|
||||
MinerTopic: "qkc_miner",
|
||||
PropagationTopic: "block_propagation",
|
||||
Errors: "error",
|
||||
}
|
||||
}
|
||||
|
||||
type GenesisAddress struct {
|
||||
Address string `json:"address"`
|
||||
PrivKey string `json:"key"`
|
||||
}
|
||||
|
||||
func loadGenesisAddrs(file string) ([]GenesisAddress, error) {
|
||||
if _, err := os.Stat(file); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
fp, err := os.Open(file)
|
||||
if err != nil {
|
||||
log.Warn("loadGenesisAddr", "file", file, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
defer fp.Close()
|
||||
var addresses []GenesisAddress
|
||||
decoder := json.NewDecoder(fp)
|
||||
for decoder.More() {
|
||||
err := decoder.Decode(&addresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// Update ShardConfig.GENESIS.ALLOC
|
||||
func UpdateGenesisAlloc(cluserConfig *ClusterConfig) error {
|
||||
if cluserConfig.GenesisDir == "" {
|
||||
return nil
|
||||
}
|
||||
loadtestFile := filepath.Join(cluserConfig.GenesisDir, testFile)
|
||||
qkcConfig := cluserConfig.Quarkchain
|
||||
|
||||
eight := new(big.Int).SetUint64(100000000)
|
||||
genesis := new(big.Int).Mul(new(big.Int).SetUint64(1000000), params.DenomsValue.Ether)
|
||||
|
||||
qetc := new(big.Int).Mul(new(big.Int).SetUint64(2), params.DenomsValue.Ether)
|
||||
qetc = new(big.Int).Mul(qetc, eight)
|
||||
|
||||
qfb := new(big.Int).Mul(new(big.Int).SetUint64(3), params.DenomsValue.Ether)
|
||||
qfb = new(big.Int).Mul(qfb, eight)
|
||||
|
||||
qaapl := new(big.Int).Mul(new(big.Int).SetUint64(4), params.DenomsValue.Ether)
|
||||
qaapl = new(big.Int).Mul(qaapl, eight)
|
||||
|
||||
qtsla := new(big.Int).Mul(new(big.Int).SetUint64(5), params.DenomsValue.Ether)
|
||||
qtsla = new(big.Int).Mul(qtsla, eight)
|
||||
|
||||
balances := map[string]*big.Int{
|
||||
qkcConfig.GenesisToken: genesis,
|
||||
"QETC": qetc,
|
||||
"QFB": qfb,
|
||||
"QAAPL": qaapl,
|
||||
"QTSLA": qtsla,
|
||||
}
|
||||
|
||||
for chainId := 0; chainId < int(qkcConfig.ChainSize); chainId++ {
|
||||
allocFile := filepath.Join(cluserConfig.GenesisDir, fmt.Sprintf(templateFile, chainId))
|
||||
addresses, err := loadGenesisAddrs(allocFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf(tempErrMsg, allocFile, err)
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
address, err := account.CreatAddressFromBytes(common.FromHex(addr.Address))
|
||||
if err != nil {
|
||||
return fmt.Errorf(tempErrMsg, allocFile, err)
|
||||
}
|
||||
fullShardId, err := qkcConfig.GetFullShardIdByFullShardKey(address.FullShardKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shard, ok := qkcConfig.shards[fullShardId]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
allocation := Allocation{
|
||||
Balances: balances,
|
||||
}
|
||||
shard.Genesis.Alloc[address] = allocation
|
||||
}
|
||||
log.Debug("Load template genesis accounts", "chain id", chainId, "imported", len(addresses), "config file", allocFile)
|
||||
}
|
||||
|
||||
items, err := loadGenesisAddrs(loadtestFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf(testErrMsg, loadtestFile, err)
|
||||
}
|
||||
for _, item := range items {
|
||||
bytes := common.FromHex(item.Address)
|
||||
for fullShardId, shardCfg := range qkcConfig.shards {
|
||||
addr := account.NewAddress(common.BytesToAddress(bytes[:20]), fullShardId)
|
||||
allocation := Allocation{
|
||||
Balances: balances,
|
||||
}
|
||||
shardCfg.Genesis.Alloc[addr] = allocation
|
||||
}
|
||||
}
|
||||
log.Info("Loadtest accounts", "loadtest file", loadtestFile, "imported", len(items))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadtestAccounts(genesisDir string) []*account.Account {
|
||||
var (
|
||||
accounts = make([]*account.Account, 0)
|
||||
err error
|
||||
)
|
||||
if genesisDir == "" {
|
||||
return nil
|
||||
}
|
||||
loadtestFile := filepath.Join(genesisDir, testFile)
|
||||
items, err := loadGenesisAddrs(loadtestFile)
|
||||
if err != nil {
|
||||
log.Error("load test file", "err", err)
|
||||
return nil
|
||||
}
|
||||
for _, item := range items {
|
||||
key := account.BytesToIdentityKey(common.FromHex(item.PrivKey))
|
||||
acc, err := account.NewAccountWithKey(key)
|
||||
if err != nil {
|
||||
log.Error("create account by key", "err", err)
|
||||
return nil
|
||||
}
|
||||
accounts = append(accounts, &acc)
|
||||
}
|
||||
return accounts
|
||||
}
|
||||
278
qkc/config/config_test.go
Normal file
278
qkc/config/config_test.go
Normal file
File diff suppressed because one or more lines are too long
211
qkc/config/shard_config.go
Normal file
211
qkc/config/shard_config.go
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/cluster/config (byte-compatible).
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/qkc/account"
|
||||
qcom "github.com/ethereum/go-ethereum/qkc/common"
|
||||
)
|
||||
|
||||
type ShardGenesis struct {
|
||||
RootHeight uint32 `json:"ROOT_HEIGHT"`
|
||||
Version uint32 `json:"VERSION"`
|
||||
Height uint64 `json:"HEIGHT"`
|
||||
HashPrevMinorBlock string `json:"HASH_PREV_MINOR_BLOCK"`
|
||||
HashMerkleRoot string `json:"HASH_MERKLE_ROOT"`
|
||||
ExtraData []byte `json:"-"`
|
||||
Timestamp uint64 `json:"TIMESTAMP"`
|
||||
Difficulty uint64 `json:"DIFFICULTY"`
|
||||
GasLimit uint64 `json:"GAS_LIMIT"`
|
||||
Nonce uint32 `json:"NONCE"`
|
||||
Alloc map[account.Address]Allocation `json:"-"`
|
||||
}
|
||||
|
||||
func NewShardGenesis() *ShardGenesis {
|
||||
return &ShardGenesis{
|
||||
RootHeight: 0,
|
||||
Version: 0,
|
||||
Height: 0,
|
||||
HashPrevMinorBlock: "",
|
||||
HashMerkleRoot: "",
|
||||
ExtraData: common.FromHex("497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73"),
|
||||
Timestamp: NewRootGenesis().Timestamp,
|
||||
Difficulty: 10000,
|
||||
GasLimit: 30000 * 400,
|
||||
Nonce: 0,
|
||||
Alloc: make(map[account.Address]Allocation),
|
||||
}
|
||||
}
|
||||
|
||||
type Allocation struct {
|
||||
Balances map[string]*big.Int
|
||||
Code []byte
|
||||
Storage map[common.Hash]common.Hash
|
||||
}
|
||||
|
||||
type AllocMarshalling = struct {
|
||||
Balances map[string]*big.Int `json:"balances"`
|
||||
Code string `json:"code"`
|
||||
Storage map[storageJSON]storageJSON `json:"storage"`
|
||||
}
|
||||
|
||||
func (a Allocation) MarshalJSON() ([]byte, error) {
|
||||
var jsonConfig AllocMarshalling
|
||||
if a.Balances != nil {
|
||||
jsonConfig.Balances = a.Balances
|
||||
}
|
||||
if a.Code != nil {
|
||||
jsonConfig.Code = common.Bytes2Hex(a.Code)
|
||||
}
|
||||
if a.Storage != nil {
|
||||
jsonConfig.Storage = make(map[storageJSON]storageJSON, len(a.Storage))
|
||||
for k, v := range a.Storage {
|
||||
jsonConfig.Storage[storageJSON(k)] = storageJSON(v)
|
||||
}
|
||||
}
|
||||
return json.Marshal(jsonConfig)
|
||||
}
|
||||
|
||||
func (a *Allocation) UnmarshalJSON(input []byte) error {
|
||||
if !strings.Contains(string(input), "balances") &&
|
||||
!strings.Contains(string(input), "code") &&
|
||||
!strings.Contains(string(input), "storage") {
|
||||
var jsonConfig map[string]*big.Int
|
||||
if err := json.Unmarshal(input, &jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
//# backward compatible:
|
||||
//# v1: {addr: {QKC: 1234}}
|
||||
//# v2: {addr: {balances: {QKC: 1234}, code: 0x, storage: {0x12: 0x34}}}
|
||||
a.Balances = jsonConfig
|
||||
return nil
|
||||
}
|
||||
|
||||
var jsonConfig AllocMarshalling
|
||||
if err := json.Unmarshal(input, &jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if jsonConfig.Balances != nil {
|
||||
a.Balances = jsonConfig.Balances
|
||||
}
|
||||
if jsonConfig.Code != "" {
|
||||
a.Code = common.FromHex(jsonConfig.Code)
|
||||
}
|
||||
if jsonConfig.Storage != nil {
|
||||
a.Storage = make(map[common.Hash]common.Hash, len(jsonConfig.Storage))
|
||||
for k, v := range jsonConfig.Storage {
|
||||
a.Storage[common.Hash(k)] = common.Hash(v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
||||
// unmarshaling from hex.
|
||||
type storageJSON common.Hash
|
||||
|
||||
func (h *storageJSON) UnmarshalText(text []byte) error {
|
||||
text = bytes.TrimPrefix(text, []byte("0x"))
|
||||
if len(text) > 64 {
|
||||
return fmt.Errorf("too many hex characters in storage key/value %q", text)
|
||||
}
|
||||
offset := len(h) - len(text)/2 // pad on the left
|
||||
if _, err := hex.Decode(h[offset:], text); err != nil {
|
||||
fmt.Println(err)
|
||||
return fmt.Errorf("invalid hex storage key/value %q", text)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h storageJSON) MarshalText() ([]byte, error) {
|
||||
return hexutil.Bytes(h[:]).MarshalText()
|
||||
}
|
||||
|
||||
type ShardGenesisAlias ShardGenesis
|
||||
|
||||
func (s *ShardGenesis) MarshalJSON() ([]byte, error) {
|
||||
alloc := make(map[string]Allocation)
|
||||
for addr, val := range s.Alloc {
|
||||
alloc[string(addr.ToHex())] = val
|
||||
}
|
||||
jsonConfig := struct {
|
||||
ShardGenesisAlias
|
||||
ExtraData string `json:"EXTRA_DATA"`
|
||||
Alloc map[string]Allocation `json:"ALLOC"`
|
||||
}{ShardGenesisAlias(*s), common.Bytes2Hex(s.ExtraData), alloc}
|
||||
return json.Marshal(jsonConfig)
|
||||
}
|
||||
|
||||
func (s *ShardGenesis) UnmarshalJSON(input []byte) error {
|
||||
var jsonConfig struct {
|
||||
ShardGenesisAlias
|
||||
ExtraData string `json:"EXTRA_DATA"`
|
||||
Alloc map[string]Allocation `json:"ALLOC"`
|
||||
}
|
||||
if err := json.Unmarshal(input, &jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = ShardGenesis(jsonConfig.ShardGenesisAlias)
|
||||
s.ExtraData = common.Hex2Bytes(jsonConfig.ExtraData)
|
||||
s.Alloc = make(map[account.Address]Allocation)
|
||||
for addr, val := range jsonConfig.Alloc {
|
||||
address, err := account.CreatAddressFromBytes(common.FromHex(addr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Alloc[address] = val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ShardConfig struct {
|
||||
ShardID uint32
|
||||
rootConfig *RootConfig
|
||||
*ChainConfig
|
||||
}
|
||||
|
||||
func NewShardConfig(chainCfg *ChainConfig) *ShardConfig {
|
||||
var cfg = new(ChainConfig)
|
||||
_ = qcom.DeepCopy(cfg, chainCfg)
|
||||
shardConfig := &ShardConfig{
|
||||
ShardID: 0,
|
||||
ChainConfig: cfg,
|
||||
}
|
||||
return shardConfig
|
||||
}
|
||||
|
||||
func (s *ShardConfig) SetRootConfig(value *RootConfig) {
|
||||
s.rootConfig = value
|
||||
}
|
||||
|
||||
func (s *ShardConfig) GetRootConfig() *RootConfig {
|
||||
return s.rootConfig
|
||||
}
|
||||
|
||||
func (s *ShardConfig) MaxBlocksPerShardInOneRootBlock() uint32 {
|
||||
return s.rootConfig.ConsensusConfig.TargetBlockTime/
|
||||
s.ConsensusConfig.TargetBlockTime + s.ExtraShardBlocksInRootBlock
|
||||
}
|
||||
|
||||
func (s *ShardConfig) MaxStaleMinorBlockHeightDiff() uint64 {
|
||||
return s.rootConfig.MaxStaleRootBlockHeightDiff *
|
||||
uint64(s.rootConfig.ConsensusConfig.TargetBlockTime) /
|
||||
uint64(s.ConsensusConfig.TargetBlockTime)
|
||||
}
|
||||
|
||||
func (s *ShardConfig) MaxMinorBlocksInMemory() uint64 {
|
||||
return s.MaxStaleMinorBlockHeightDiff() * 2
|
||||
}
|
||||
|
||||
func (s *ShardConfig) GetFullShardId() uint32 {
|
||||
return (s.ChainID << 16) | s.ShardSize | s.ShardID
|
||||
}
|
||||
76
qkc/config/slave_config.go
Normal file
76
qkc/config/slave_config.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Ported from github.com/QuarkChain/goquarkchain/cluster/config. The WebSocket
|
||||
// JSON-RPC field is adapted to pyquarkchain's WEBSOCKET_JSON_RPC_PORT (a single
|
||||
// optional port) instead of goquarkchain's WEBSOCKET_JSON_RPC_PORT_LIST, since
|
||||
// the goshard slave reads a pyquarkchain master's cluster config.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/qkc/common/hexutil"
|
||||
)
|
||||
|
||||
type SlaveConfig struct {
|
||||
IP string `json:"HOST"` // DEFAULT_HOST
|
||||
Port uint16 `json:"PORT"` // 38392
|
||||
ID string `json:"ID"`
|
||||
WSPort *uint16 `json:"WEBSOCKET_JSON_RPC_PORT"` // pyquarkchain: optional (default None)
|
||||
FullShardList []uint32 `json:"-"`
|
||||
ChainMaskListForBackward []uint32 `json:"-"`
|
||||
}
|
||||
|
||||
type SlaveConfigAlias SlaveConfig
|
||||
|
||||
func (s *SlaveConfig) MarshalJSON() ([]byte, error) {
|
||||
shardMaskList := make([]hexutil.Uint, len(s.FullShardList))
|
||||
for i, m := range s.FullShardList {
|
||||
shardMaskList[i] = hexutil.Uint(m)
|
||||
}
|
||||
jsonConfig := struct {
|
||||
SlaveConfigAlias
|
||||
ShardMaskList []hexutil.Uint `json:"FULL_SHARD_ID_LIST"`
|
||||
}{SlaveConfigAlias(*s), shardMaskList}
|
||||
return json.Marshal(jsonConfig)
|
||||
}
|
||||
|
||||
func (s *SlaveConfig) UnmarshalJSON(input []byte) error {
|
||||
var jsonConfig struct {
|
||||
SlaveConfigAlias
|
||||
ChainMaskListJson *[]uint32 `json:"CHAIN_MASK_LIST"`
|
||||
FullShardListJson *[]hexutil.Uint `json:"FULL_SHARD_ID_LIST"`
|
||||
}
|
||||
if err := json.Unmarshal(input, &jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = SlaveConfig(jsonConfig.SlaveConfigAlias)
|
||||
|
||||
if jsonConfig.ChainMaskListJson != nil && jsonConfig.FullShardListJson != nil {
|
||||
return errors.New("Can only have either FULL_SHARD_ID_LIST or CHAIN_MASK_LIST")
|
||||
} else if jsonConfig.FullShardListJson != nil {
|
||||
s.FullShardList = make([]uint32, len(*jsonConfig.FullShardListJson))
|
||||
for k, v := range *jsonConfig.FullShardListJson {
|
||||
s.FullShardList[k] = uint32(v)
|
||||
}
|
||||
} else if jsonConfig.ChainMaskListJson != nil {
|
||||
//handle it after call SlaveConfig.UnmarshalJSON
|
||||
// can not get ClusterConfig.QuarkChain.Chains config
|
||||
s.FullShardList = nil
|
||||
s.ChainMaskListForBackward = make([]uint32, len(*jsonConfig.ChainMaskListJson))
|
||||
for k, v := range *jsonConfig.ChainMaskListJson {
|
||||
s.ChainMaskListForBackward[k] = v
|
||||
}
|
||||
} else {
|
||||
return errors.New("Missing FULL_SHARD_ID_LIST (or CHAIN_MASK_LIST as legacy config)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewDefaultSlaveConfig() *SlaveConfig {
|
||||
slaveConfig := SlaveConfig{
|
||||
IP: DefaultHost,
|
||||
Port: slavePort,
|
||||
}
|
||||
return &slaveConfig
|
||||
}
|
||||
238
qkc/config/test_config.json
Normal file
238
qkc/config/test_config.json
Normal file
File diff suppressed because one or more lines are too long
221
qkc/config/testdata/cluster_config_template.json
vendored
Executable file
221
qkc/config/testdata/cluster_config_template.json
vendored
Executable file
|
|
@ -0,0 +1,221 @@
|
|||
{
|
||||
"P2P_PORT": 38291,
|
||||
"JSON_RPC_PORT": 38391,
|
||||
"PRIVATE_JSON_RPC_PORT": 38491,
|
||||
"ENABLE_TRANSACTION_HISTORY": false,
|
||||
"DB_PATH_ROOT": "./qkc-data/testnet24",
|
||||
"LOG_LEVEL": "info",
|
||||
"START_SIMULATED_MINING": false,
|
||||
"CLEAN": false,
|
||||
"GENESIS_DIR": "/dev/null",
|
||||
"QUARKCHAIN": {
|
||||
"CHAIN_SIZE": 3,
|
||||
"MAX_NEIGHBORS": 32,
|
||||
"NETWORK_ID": 24,
|
||||
"TRANSACTION_QUEUE_SIZE_LIMIT_PER_SHARD": 10000,
|
||||
"BLOCK_EXTRA_DATA_SIZE_LIMIT": 1024,
|
||||
"GUARDIAN_PUBLIC_KEY": "ab856abd0983a82972021e454fcf66ed5940ed595b0898bcd75cbe2d0a51a00f5358b566df22395a2a8bf6c022c1d51a2c3defe654e91a8d244947783029694d",
|
||||
"ROOT_SIGNER_PRIVATE_KEY": null,
|
||||
"P2P_PROTOCOL_VERSION": 0,
|
||||
"P2P_COMMAND_SIZE_LIMIT": 4294967295,
|
||||
"SKIP_ROOT_DIFFICULTY_CHECK": false,
|
||||
"SKIP_MINOR_DIFFICULTY_CHECK": false,
|
||||
"GENESIS_TOKEN": "TQKC",
|
||||
"ROOT": {
|
||||
"MAX_STALE_ROOT_BLOCK_HEIGHT_DIFF": 60,
|
||||
"CONSENSUS_TYPE": "POW_ETHASH",
|
||||
"CONSENSUS_CONFIG": {
|
||||
"TARGET_BLOCK_TIME": 60,
|
||||
"REMOTE_MINE": true
|
||||
},
|
||||
"GENESIS": {
|
||||
"VERSION": 0,
|
||||
"HEIGHT": 0,
|
||||
"HASH_PREV_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"TIMESTAMP": 1519147489,
|
||||
"DIFFICULTY": 1000000000000,
|
||||
"NONCE": 0
|
||||
},
|
||||
"COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000",
|
||||
"COINBASE_AMOUNT": 120000000000000000000,
|
||||
"DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 40,
|
||||
"DIFFICULTY_ADJUSTMENT_FACTOR": 1024
|
||||
},
|
||||
"CHAINS": [
|
||||
{
|
||||
"CHAIN_ID": 0,
|
||||
"SHARD_SIZE": 2,
|
||||
"DEFAULT_CHAIN_TOKEN": "TQKC",
|
||||
"CONSENSUS_TYPE": "POW_ETHASH",
|
||||
"CONSENSUS_CONFIG": {
|
||||
"TARGET_BLOCK_TIME": 10,
|
||||
"REMOTE_MINE": true
|
||||
},
|
||||
"GENESIS": {
|
||||
"ROOT_HEIGHT": 0,
|
||||
"VERSION": 0,
|
||||
"HEIGHT": 0,
|
||||
"HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73",
|
||||
"TIMESTAMP": 1519147489,
|
||||
"DIFFICULTY": 5000000000,
|
||||
"GAS_LIMIT": 12000000,
|
||||
"NONCE": 0,
|
||||
"ALLOC": {}
|
||||
},
|
||||
"COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000",
|
||||
"COINBASE_AMOUNT": 5000000000000000000,
|
||||
"GAS_LIMIT_EMA_DENOMINATOR": 1024,
|
||||
"GAS_LIMIT_ADJUSTMENT_FACTOR": 1024,
|
||||
"GAS_LIMIT_MINIMUM": 5000,
|
||||
"GAS_LIMIT_MAXIMUM": 9223372036854775807,
|
||||
"GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR": 3,
|
||||
"GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR": 2,
|
||||
"DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7,
|
||||
"DIFFICULTY_ADJUSTMENT_FACTOR": 512,
|
||||
"EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 30,
|
||||
"POSW_CONFIG": {
|
||||
"ENABLED": false,
|
||||
"DIFF_DIVIDER": 20,
|
||||
"WINDOW_SIZE": 256,
|
||||
"TOTAL_STAKE_PER_BLOCK": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"CHAIN_ID": 1,
|
||||
"SHARD_SIZE": 2,
|
||||
"DEFAULT_CHAIN_TOKEN": "TQKC",
|
||||
"CONSENSUS_TYPE": "POW_ETHASH",
|
||||
"CONSENSUS_CONFIG": {
|
||||
"TARGET_BLOCK_TIME": 10,
|
||||
"REMOTE_MINE": true
|
||||
},
|
||||
"GENESIS": {
|
||||
"ROOT_HEIGHT": 0,
|
||||
"VERSION": 0,
|
||||
"HEIGHT": 0,
|
||||
"HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73",
|
||||
"TIMESTAMP": 1519147489,
|
||||
"DIFFICULTY": 5000000000,
|
||||
"GAS_LIMIT": 12000000,
|
||||
"NONCE": 0,
|
||||
"ALLOC": {}
|
||||
},
|
||||
"COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000",
|
||||
"COINBASE_AMOUNT": 5000000000000000000,
|
||||
"GAS_LIMIT_EMA_DENOMINATOR": 1024,
|
||||
"GAS_LIMIT_ADJUSTMENT_FACTOR": 1024,
|
||||
"GAS_LIMIT_MINIMUM": 5000,
|
||||
"GAS_LIMIT_MAXIMUM": 9223372036854775807,
|
||||
"GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR": 3,
|
||||
"GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR": 2,
|
||||
"DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7,
|
||||
"DIFFICULTY_ADJUSTMENT_FACTOR": 512,
|
||||
"EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 30,
|
||||
"POSW_CONFIG": {
|
||||
"ENABLED": true,
|
||||
"DIFF_DIVIDER": 20,
|
||||
"WINDOW_SIZE": 256,
|
||||
"TOTAL_STAKE_PER_BLOCK": 2560000000000000000000000
|
||||
}
|
||||
},
|
||||
{
|
||||
"CHAIN_ID": 2,
|
||||
"SHARD_SIZE": 2,
|
||||
"DEFAULT_CHAIN_TOKEN": "TQKC",
|
||||
"CONSENSUS_TYPE": "POW_ETHASH",
|
||||
"CONSENSUS_CONFIG": {
|
||||
"TARGET_BLOCK_TIME": 10,
|
||||
"REMOTE_MINE": true
|
||||
},
|
||||
"GENESIS": {
|
||||
"ROOT_HEIGHT": 0,
|
||||
"VERSION": 0,
|
||||
"HEIGHT": 0,
|
||||
"HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73",
|
||||
"TIMESTAMP": 1519147489,
|
||||
"DIFFICULTY": 5000000000,
|
||||
"GAS_LIMIT": 12000000,
|
||||
"NONCE": 0,
|
||||
"ALLOC": {}
|
||||
},
|
||||
"COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000",
|
||||
"COINBASE_AMOUNT": 5000000000000000000,
|
||||
"GAS_LIMIT_EMA_DENOMINATOR": 1024,
|
||||
"GAS_LIMIT_ADJUSTMENT_FACTOR": 1024,
|
||||
"GAS_LIMIT_MINIMUM": 5000,
|
||||
"GAS_LIMIT_MAXIMUM": 9223372036854775807,
|
||||
"GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR": 3,
|
||||
"GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR": 2,
|
||||
"DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7,
|
||||
"DIFFICULTY_ADJUSTMENT_FACTOR": 512,
|
||||
"EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 30,
|
||||
"POSW_CONFIG": {
|
||||
"ENABLED": true,
|
||||
"DIFF_DIVIDER": 20,
|
||||
"WINDOW_SIZE": 256,
|
||||
"TOTAL_STAKE_PER_BLOCK": 5120000000000000000000000
|
||||
}
|
||||
}
|
||||
],
|
||||
"REWARD_TAX_RATE": 0.5
|
||||
},
|
||||
"MASTER": {
|
||||
"MASTER_TO_SLAVE_CONNECT_RETRY_DELAY": 1.0
|
||||
},
|
||||
"SLAVE_LIST": [
|
||||
{
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 38000,
|
||||
"ID": "S0",
|
||||
"CHAIN_MASK_LIST": [
|
||||
4
|
||||
]
|
||||
},
|
||||
{
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 38001,
|
||||
"ID": "S1",
|
||||
"CHAIN_MASK_LIST": [
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 38002,
|
||||
"ID": "S2",
|
||||
"CHAIN_MASK_LIST": [
|
||||
6
|
||||
]
|
||||
},
|
||||
{
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 38003,
|
||||
"ID": "S3",
|
||||
"CHAIN_MASK_LIST": [
|
||||
7
|
||||
]
|
||||
}
|
||||
],
|
||||
"P2P": {
|
||||
"NEW_MODULE": true,
|
||||
"MAX_PEERS": 30,
|
||||
"BOOT_NODES": "enode://f2b808fa11e695a26489e21143b7a4e4012bae1fbbbdb4adfb72627ac591f93490c393f4a59acd7bb18a01e1a289830e5020bf6976abf3071de628be74974cce@34.215.81.118:38291,enode://5eeaae0986bb6d7e36fd3a23ae15ca371ba9e88e8545825ed587a472b5ec417c3f757ee46e643d834a7ff454855cfc28b736b8b76f2eae3ebbe61028b777971c@35.155.213.180:38291,enode://7e6a535da88c368cc47d9d6a3fc76d92aa7d3537ed66161c7dc3b6801de48d4a23ed6c8b48053b64faf715a77ec097d624c8638269dcd9339935194b7f3c3530@52.38.218.246:38291",
|
||||
"PRIV_KEY": "",
|
||||
"UPNP": true
|
||||
},
|
||||
"MONITORING": {
|
||||
"NETWORK_NAME": "",
|
||||
"CLUSTER_ID": "127.0.0.1",
|
||||
"KAFKA_REST_ADDRESS": "",
|
||||
"MINER_TOPIC": "qkc_miner",
|
||||
"PROPAGATION_TOPIC": "block_propagation",
|
||||
"ERRORS": "error"
|
||||
}
|
||||
}
|
||||
75
qkc/params/evm_params.go
Normal file
75
qkc/params/evm_params.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/params (byte-compatible).
|
||||
|
||||
package params
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethParams "github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
DenomsValue = Denoms{
|
||||
Wei: new(big.Int).SetUint64(1),
|
||||
GWei: new(big.Int).SetUint64(1000000000), //10^9
|
||||
Ether: new(big.Int).Mul(new(big.Int).SetUint64(1000000000), new(big.Int).SetUint64(1000000000)),
|
||||
}
|
||||
GCallValueTransfer = new(big.Int).SetUint64(9000)
|
||||
GtxxShardCost = GCallValueTransfer // x-shard tx deposit gas
|
||||
|
||||
DefaultStateDBGasLimit = new(big.Int).SetUint64(3141592)
|
||||
DefaultBlockGasLimit = new(big.Int).SetUint64(30000 * 400)
|
||||
|
||||
DefaultStartGas = new(big.Int).SetUint64(100 * 1000)
|
||||
DefaultGasPrice = new(big.Int).Mul(new(big.Int).SetUint64(10), DenomsValue.GWei)
|
||||
|
||||
DefaultInShardTxGasLimit = new(big.Int).SetUint64(21000)
|
||||
DefaultCrossShardTxGasLimit = new(big.Int).SetUint64(30000)
|
||||
)
|
||||
|
||||
type Denoms struct {
|
||||
Wei *big.Int
|
||||
GWei *big.Int
|
||||
Ether *big.Int
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultConstantinople = ethParams.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
DAOForkBlock: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
// goquarkchain hard-codes legacy (pre-EIP-1283) SSTORE metering in its
|
||||
// gasSStore via an `if true`, i.e. it runs Petersburg behavior in code.
|
||||
// Stock geth gates that on PetersburgBlock, so set it to reproduce
|
||||
// goquarkchain's EVM faithfully.
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
PrecompiledContractsAfterEvmEnabled = []common.Address{
|
||||
common.HexToAddress("000000000000000000000000000000514b430001"),
|
||||
common.HexToAddress("000000000000000000000000000000514b430002"),
|
||||
common.HexToAddress("000000000000000000000000000000514b430003"),
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
PrecompiledContractsMnt = []common.Address{
|
||||
common.HexToAddress("000000000000000000000000000000514b430004"),
|
||||
common.HexToAddress("000000000000000000000000000000514b430005"),
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
MAINNET_ENABLE_NON_RESERVED_NATIVE_TOKEN_CONTRACT_TIMESTAMP = uint64(1588291200)
|
||||
MAINNET_ENABLE_GENERAL_NATIVE_TOKEN_CONTRACT_TIMESTAMP = uint64(1588291200)
|
||||
MAINNET_ENABLE_POSW_STAKING_DECAY_TIMESTAMP = uint64(1588291200)
|
||||
MAINNET_ENABLE_EIP155_SIGNER_TIMESTAMP = uint64(1631577600)
|
||||
)
|
||||
51
qkc/params/version.go
Normal file
51
qkc/params/version.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Ported verbatim from github.com/QuarkChain/goquarkchain/params.
|
||||
// Modified from go-ethereum under GNU Lesser General Public License
|
||||
package params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 0 // Minor version component of the current release
|
||||
VersionPatch = 0 // Patch version component of the current release
|
||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
// Version holds the textual version string.
|
||||
var Version = func() string {
|
||||
return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
|
||||
}()
|
||||
|
||||
// VersionWithMeta holds the textual version string including the metadata.
|
||||
var VersionWithMeta = func() string {
|
||||
v := Version
|
||||
if VersionMeta != "" {
|
||||
v += "-" + VersionMeta
|
||||
}
|
||||
return v
|
||||
}()
|
||||
|
||||
// ArchiveVersion holds the textual version string used for Geth archives.
|
||||
// e.g. "1.8.11-dea1ce05" for stable releases, or
|
||||
//
|
||||
// "1.8.13-unstable-21c059b6" for unstable releases
|
||||
func ArchiveVersion(gitCommit string) string {
|
||||
vsn := Version
|
||||
if VersionMeta != "stable" {
|
||||
vsn += "-" + VersionMeta
|
||||
}
|
||||
if len(gitCommit) >= 8 {
|
||||
vsn += "-" + gitCommit[:8]
|
||||
}
|
||||
return vsn
|
||||
}
|
||||
|
||||
func VersionWithCommit(gitCommit string) string {
|
||||
vsn := VersionWithMeta
|
||||
if len(gitCommit) >= 8 {
|
||||
vsn += "-" + gitCommit[:8]
|
||||
}
|
||||
return vsn
|
||||
}
|
||||
Loading…
Reference in a new issue