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:
blockchaindevsh 2026-06-22 20:17:40 +08:00 committed by GitHub
parent 0a76f2a7fc
commit 4160ab81ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 6671 additions and 0 deletions

246
qkc/account/account.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View 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())
}

View 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"
}

View 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"
}

View 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"
}

View 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"
}

View 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
View 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

File diff suppressed because one or more lines are too long

1602
qkc/account/testdata/testBranch.json vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

91
qkc/account/types.go Normal file
View 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
}

View 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
}

View 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
View 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}
}

View 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
}

View 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
View 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
}

View 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
View 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
}

View 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
}

View 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
View 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

File diff suppressed because one or more lines are too long

211
qkc/config/shard_config.go Normal file
View 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
}

View 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

File diff suppressed because one or more lines are too long

View 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
View 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
View 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
}