Merge pull request #1706 from rlan35/mainnet_release_1005_merge_s3
Mainnet release 1005 merge s3pull/1726/head
commit
cbb2d4997f
@ -0,0 +1,24 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
) |
||||
|
||||
var ( |
||||
version string |
||||
commit string |
||||
builtAt string |
||||
builtBy string |
||||
) |
||||
|
||||
func main() { |
||||
if os.Args[1] == "-version" { |
||||
fmt.Fprintf(os.Stderr, versionS()+"\n") |
||||
os.Exit(0) |
||||
} |
||||
if err := rootCmd.Execute(); err != nil { |
||||
fmt.Println(err) |
||||
os.Exit(-1) |
||||
} |
||||
} |
@ -0,0 +1,173 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"math/big" |
||||
"net/http" |
||||
"os" |
||||
"path" |
||||
"strconv" |
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/harmony-one/bls/ffi/go/bls" |
||||
"github.com/harmony-one/harmony/accounts" |
||||
"github.com/harmony-one/harmony/accounts/keystore" |
||||
"github.com/harmony-one/harmony/internal/common" |
||||
"github.com/harmony-one/harmony/shard" |
||||
staking "github.com/harmony-one/harmony/staking/types" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
const ( |
||||
keystoreDir = ".hmy/keystore" |
||||
stakingRPC = "hmy_sendRawStakingTransaction" |
||||
) |
||||
|
||||
type staker struct{} |
||||
|
||||
var ( |
||||
queryID = 0 |
||||
s = &staker{} |
||||
localNetChain = big.NewInt(2) |
||||
dAddr = common.ParseAddr(testAccount) |
||||
) |
||||
|
||||
const ( |
||||
// Harmony protocol assume beacon chain shard is only place to send
|
||||
// staking, later need to consider logic when beacon chain shard rotates
|
||||
stakingShard = 0 |
||||
testAccount = "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" |
||||
testBLSPubKey = "b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611" |
||||
testAccountPassword = "" |
||||
) |
||||
|
||||
func (s *staker) run(cmd *cobra.Command, args []string) error { |
||||
scryptN := keystore.StandardScryptN |
||||
scryptP := keystore.StandardScryptP |
||||
ks := keystore.NewKeyStore(keystoreDir, scryptN, scryptP) |
||||
account := accounts.Account{Address: dAddr} |
||||
ks.Unlock(account, testAccountPassword) |
||||
gasPrice := big.NewInt(0) |
||||
stakePayloadMaker := func() (staking.Directive, interface{}) { |
||||
p := &bls.PublicKey{} |
||||
p.DeserializeHexStr(testBLSPubKey) |
||||
pub := shard.BlsPublicKey{} |
||||
pub.FromLibBLSPublicKey(p) |
||||
return staking.DirectiveNewValidator, staking.NewValidator{ |
||||
Description: staking.Description{ |
||||
Name: "something", |
||||
Identity: "something else", |
||||
Website: "some site, harmony.one", |
||||
SecurityContact: "mr.smith", |
||||
Details: "blah blah details", |
||||
}, |
||||
CommissionRates: staking.CommissionRates{ |
||||
Rate: staking.NewDec(100), |
||||
MaxRate: staking.NewDec(150), |
||||
MaxChangeRate: staking.NewDec(5), |
||||
}, |
||||
MinSelfDelegation: big.NewInt(10), |
||||
StakingAddress: common.Address(dAddr), |
||||
PubKey: pub, |
||||
Amount: big.NewInt(100), |
||||
} |
||||
// return message.DirectiveDelegate, message.Delegate{
|
||||
// common.Address(dAddr),
|
||||
// common.Address(dAddr),
|
||||
// big.NewInt(10),
|
||||
// }
|
||||
} |
||||
|
||||
stakingTx, err := staking.NewStakingTransaction(2, 100, gasPrice, stakePayloadMaker) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
signed, oops := ks.SignStakingTx(account, stakingTx, localNetChain) |
||||
if oops != nil { |
||||
return oops |
||||
} |
||||
enc, oops1 := rlp.EncodeToBytes(signed) |
||||
if oops1 != nil { |
||||
return oops1 |
||||
} |
||||
tx := new(staking.StakingTransaction) |
||||
if err := rlp.DecodeBytes(enc, tx); err != nil { |
||||
return err |
||||
} |
||||
fmt.Printf("In Client side: %+v\n", tx) |
||||
// return nil
|
||||
rlp.DecodeBytes(enc, tx) |
||||
hexSignature := hexutil.Encode(enc) |
||||
param := []interface{}{hexSignature} |
||||
result, reqOops := baseRequest(stakingRPC, "http://localhost:9500", param) |
||||
fmt.Println(string(result)) |
||||
return reqOops |
||||
} |
||||
|
||||
func versionS() string { |
||||
return fmt.Sprintf( |
||||
"Harmony (C) 2019. %v, version %v-%v (%v %v)", |
||||
path.Base(os.Args[0]), version, commit, builtBy, builtAt, |
||||
) |
||||
} |
||||
|
||||
func (s *staker) preRunInit(cmd *cobra.Command, args []string) error { |
||||
// Just in case need to do some kind of setup that needs to propagate downward
|
||||
return nil |
||||
} |
||||
|
||||
func baseRequest(method, node string, params interface{}) ([]byte, error) { |
||||
requestBody, _ := json.Marshal(map[string]interface{}{ |
||||
"jsonrpc": "2.0", |
||||
"id": strconv.Itoa(queryID), |
||||
"method": method, |
||||
"params": params, |
||||
}) |
||||
resp, err := http.Post(node, "application/json", bytes.NewBuffer(requestBody)) |
||||
fmt.Printf("URL: %s, Request Body: %s\n\n", node, string(requestBody)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer resp.Body.Close() |
||||
body, err := ioutil.ReadAll(resp.Body) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
queryID++ |
||||
fmt.Printf("URL: %s, Response Body: %s\n\n", node, string(body)) |
||||
return body, err |
||||
} |
||||
|
||||
func init() { |
||||
|
||||
rootCmd.AddCommand(&cobra.Command{ |
||||
Use: "staking-iterate", |
||||
Short: "run through staking process", |
||||
PersistentPreRunE: s.preRunInit, |
||||
RunE: s.run, |
||||
}) |
||||
|
||||
rootCmd.AddCommand(&cobra.Command{ |
||||
Use: "version", |
||||
Short: "Show version", |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
fmt.Fprintf(os.Stderr, versionS()+"\n") |
||||
os.Exit(0) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
var ( |
||||
rootCmd = &cobra.Command{ |
||||
Use: "staking-standalone", |
||||
SilenceUsage: true, |
||||
Long: "testing staking quickly", |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
cmd.Help() |
||||
}, |
||||
} |
||||
) |
@ -1,4 +1,4 @@ |
||||
package p2p |
||||
package nodeconfig |
||||
|
||||
import "testing" |
||||
|
@ -0,0 +1,247 @@ |
||||
package types |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math/big" |
||||
"strings" |
||||
|
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/harmony-one/harmony/internal/common" |
||||
) |
||||
|
||||
// DVPair is struct that just has a delegator-validator pair with no other data.
|
||||
// It is intended to be used as a marshalable pointer. For example, a DVPair can be used to construct the
|
||||
// key to getting an UnbondingDelegation from state.
|
||||
type DVPair struct { |
||||
DelegatorAddress common.Address |
||||
ValidatorAddress common.Address |
||||
} |
||||
|
||||
// DVVTriplet is struct that just has a delegator-validator-validator triplet with no other data.
|
||||
// It is intended to be used as a marshalable pointer. For example, a DVVTriplet can be used to construct the
|
||||
// key to getting a Redelegation from state.
|
||||
type DVVTriplet struct { |
||||
DelegatorAddress common.Address |
||||
ValidatorSrcAddress common.Address |
||||
ValidatorDstAddress common.Address |
||||
} |
||||
|
||||
// Delegation represents the bond with tokens held by an account. It is
|
||||
// owned by one delegator, and is associated with the voting power of one
|
||||
// validator.
|
||||
type Delegation struct { |
||||
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"` |
||||
ValidatorAddress common.Address `json:"validator_address" yaml:"validator_address"` |
||||
Amount *big.Int `json:"amount" yaml:"amount"` |
||||
} |
||||
|
||||
// NewDelegation creates a new delegation object
|
||||
func NewDelegation(delegatorAddr common.Address, validatorAddr common.Address, |
||||
amount *big.Int) Delegation { |
||||
|
||||
return Delegation{ |
||||
DelegatorAddress: delegatorAddr, |
||||
ValidatorAddress: validatorAddr, |
||||
Amount: amount, |
||||
} |
||||
} |
||||
|
||||
// MarshalDelegation return the delegation
|
||||
func MarshalDelegation(delegation Delegation) ([]byte, error) { |
||||
return rlp.EncodeToBytes(delegation) |
||||
} |
||||
|
||||
// UnmarshalDelegation return the delegation
|
||||
func UnmarshalDelegation(by []byte) (*Delegation, error) { |
||||
decoded := &Delegation{} |
||||
err := rlp.DecodeBytes(by, decoded) |
||||
return decoded, err |
||||
} |
||||
|
||||
// GetDelegatorAddr returns DelegatorAddr
|
||||
func (d Delegation) GetDelegatorAddr() common.Address { return d.DelegatorAddress } |
||||
|
||||
// GetValidatorAddr returns ValidatorAddr
|
||||
func (d Delegation) GetValidatorAddr() common.Address { return d.ValidatorAddress } |
||||
|
||||
// GetAmount returns amount of a delegation
|
||||
func (d Delegation) GetAmount() *big.Int { return d.Amount } |
||||
|
||||
// String returns a human readable string representation of a Delegation.
|
||||
func (d Delegation) String() string { |
||||
return fmt.Sprintf(`Delegation: |
||||
Delegator: %s |
||||
Validator: %s |
||||
Amount: %s`, d.DelegatorAddress, |
||||
d.ValidatorAddress, d.Amount) |
||||
} |
||||
|
||||
// Delegations is a collection of delegations
|
||||
type Delegations []Delegation |
||||
|
||||
// String returns the string representation of a list of delegations
|
||||
func (d Delegations) String() (out string) { |
||||
for _, del := range d { |
||||
out += del.String() + "\n" |
||||
} |
||||
return strings.TrimSpace(out) |
||||
} |
||||
|
||||
// UnbondingDelegation stores all of a single delegator's unbonding bonds
|
||||
// for a single validator in an time-ordered list
|
||||
type UnbondingDelegation struct { |
||||
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"` // delegator
|
||||
ValidatorAddress common.Address `json:"validator_address" yaml:"validator_address"` // validator unbonding from operator addr
|
||||
Entries []UnbondingDelegationEntry `json:"entries" yaml:"entries"` // unbonding delegation entries
|
||||
} |
||||
|
||||
// UnbondingDelegationEntry - entry to an UnbondingDelegation
|
||||
type UnbondingDelegationEntry struct { |
||||
ExitEpoch *big.Int `json:"exit_epoch" yaml:"exit_epoch"` // epoch which the unbonding begins
|
||||
Amount *big.Int `json:"amount" yaml:"amount"` // atoms to receive at completion
|
||||
} |
||||
|
||||
// NewUnbondingDelegation - create a new unbonding delegation object
|
||||
func NewUnbondingDelegation(delegatorAddr common.Address, |
||||
validatorAddr common.Address, epoch *big.Int, amt *big.Int) UnbondingDelegation { |
||||
|
||||
entry := NewUnbondingDelegationEntry(epoch, amt) |
||||
return UnbondingDelegation{ |
||||
DelegatorAddress: delegatorAddr, |
||||
ValidatorAddress: validatorAddr, |
||||
Entries: []UnbondingDelegationEntry{entry}, |
||||
} |
||||
} |
||||
|
||||
// NewUnbondingDelegationEntry - create a new unbonding delegation object
|
||||
func NewUnbondingDelegationEntry(epoch *big.Int, amt *big.Int) UnbondingDelegationEntry { |
||||
return UnbondingDelegationEntry{ |
||||
ExitEpoch: epoch, |
||||
Amount: amt, |
||||
} |
||||
} |
||||
|
||||
// AddEntry - append entry to the unbonding delegation
|
||||
// if there exists same ExitEpoch entry, merge the amount
|
||||
// TODO: check the total amount not exceed the staking amount call this function
|
||||
func (d *UnbondingDelegation) AddEntry(epoch *big.Int, amt *big.Int) { |
||||
entry := NewUnbondingDelegationEntry(epoch, amt) |
||||
for i := range d.Entries { |
||||
if d.Entries[i].ExitEpoch == entry.ExitEpoch { |
||||
d.Entries[i].Amount.Add(d.Entries[i].Amount, entry.Amount) |
||||
return |
||||
} |
||||
} |
||||
// same exit epoch entry not found
|
||||
d.Entries = append(d.Entries, entry) |
||||
return |
||||
} |
||||
|
||||
// String returns a human readable string representation of an UnbondingDelegation.
|
||||
func (d UnbondingDelegation) String() string { |
||||
out := fmt.Sprintf(`Unbonding Delegations between: |
||||
Delegator: %s |
||||
Validator: %s |
||||
Entries:`, d.DelegatorAddress, d.ValidatorAddress) |
||||
for i, entry := range d.Entries { |
||||
out += fmt.Sprintf(` Unbonding Delegation %d: |
||||
ExitEpoch: %v |
||||
Amount: %s`, i, entry.ExitEpoch, entry.Amount) |
||||
} |
||||
return out |
||||
} |
||||
|
||||
// UnbondingDelegations is a collection of UnbondingDelegation
|
||||
type UnbondingDelegations []UnbondingDelegation |
||||
|
||||
func (ubds UnbondingDelegations) String() (out string) { |
||||
for _, u := range ubds { |
||||
out += u.String() + "\n" |
||||
} |
||||
return strings.TrimSpace(out) |
||||
} |
||||
|
||||
// Redelegation contains the list of a particular delegator's
|
||||
// redelegating bonds from a particular source validator to a
|
||||
// particular destination validator
|
||||
type Redelegation struct { |
||||
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"` // delegator
|
||||
ValidatorSrcAddress common.Address `json:"validator_src_address" yaml:"validator_src_address"` // validator redelegation source operator addr
|
||||
ValidatorDstAddress common.Address `json:"validator_dst_address" yaml:"validator_dst_address"` // validator redelegation destination operator addr
|
||||
Entries []RedelegationEntry `json:"entries" yaml:"entries"` // redelegation entries
|
||||
} |
||||
|
||||
// RedelegationEntry - entry to a Redelegation
|
||||
type RedelegationEntry struct { |
||||
Epoch *big.Int `json:"epoch" yaml:"epoch"` // epoch at which the redelegation took place
|
||||
Amount *big.Int `json:"amount" yaml:"amount"` // amount of destination-validator tokens created by redelegation
|
||||
} |
||||
|
||||
// NewRedelegation - create a new redelegation object
|
||||
func NewRedelegation(delegatorAddr common.Address, validatorSrcAddr, |
||||
validatorDstAddr common.Address, epoch *big.Int, amt *big.Int) Redelegation { |
||||
entry := NewRedelegationEntry(epoch, amt) |
||||
return Redelegation{ |
||||
DelegatorAddress: delegatorAddr, |
||||
ValidatorSrcAddress: validatorSrcAddr, |
||||
ValidatorDstAddress: validatorDstAddr, |
||||
Entries: []RedelegationEntry{entry}, |
||||
} |
||||
} |
||||
|
||||
// NewRedelegationEntry - create a new redelegation object
|
||||
func NewRedelegationEntry(epoch *big.Int, amt *big.Int) RedelegationEntry { |
||||
return RedelegationEntry{ |
||||
Epoch: epoch, |
||||
Amount: amt, |
||||
} |
||||
} |
||||
|
||||
// AddEntry - append entry to the unbonding delegation
|
||||
// Merge if has same epoch field
|
||||
func (d *Redelegation) AddEntry(epoch *big.Int, amt *big.Int) { |
||||
entry := NewRedelegationEntry(epoch, amt) |
||||
|
||||
for i := range d.Entries { |
||||
if d.Entries[i].Epoch == entry.Epoch { |
||||
d.Entries[i].Amount.Add(d.Entries[i].Amount, entry.Amount) |
||||
return |
||||
} |
||||
} |
||||
// same epoch entry not found
|
||||
d.Entries = append(d.Entries, entry) |
||||
return |
||||
} |
||||
|
||||
// String returns a human readable string representation of a Redelegation.
|
||||
func (d Redelegation) String() string { |
||||
out := fmt.Sprintf(`Redelegations between: |
||||
Delegator: %s |
||||
Source Validator: %s |
||||
Destination Validator: %s |
||||
Entries: |
||||
`, |
||||
d.DelegatorAddress, d.ValidatorSrcAddress, d.ValidatorDstAddress, |
||||
) |
||||
|
||||
for i, entry := range d.Entries { |
||||
out += fmt.Sprintf(` Redelegation Entry #%d: |
||||
Epoch: %v |
||||
Amount: %v |
||||
`, |
||||
i, entry.Epoch, entry.Amount, |
||||
) |
||||
} |
||||
|
||||
return strings.TrimRight(out, "\n") |
||||
} |
||||
|
||||
// Redelegations are a collection of Redelegation
|
||||
type Redelegations []Redelegation |
||||
|
||||
func (d Redelegations) String() (out string) { |
||||
for _, red := range d { |
||||
out += red.String() + "\n" |
||||
} |
||||
return strings.TrimSpace(out) |
||||
} |
@ -1,218 +1,207 @@ |
||||
package types |
||||
|
||||
import ( |
||||
"math/big" |
||||
"reflect" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/harmony-one/harmony/crypto/bls" |
||||
"github.com/harmony-one/harmony/internal/common" |
||||
"github.com/harmony-one/harmony/shard" |
||||
) |
||||
|
||||
var ( |
||||
minSelfDelegation = big.NewInt(1000) |
||||
stakeAmount = big.NewInt(2000) |
||||
delegateAmount = big.NewInt(500) |
||||
validatorAddress = common.MustBech32ToAddress("one1pdv9lrdwl0rg5vglh4xtyrv3wjk3wsqket7zxy") |
||||
validatorAddress2 = common.MustBech32ToAddress("one1d2rngmem4x2c6zxsjjz29dlah0jzkr0k2n88wc") |
||||
delegatorAddress = common.MustBech32ToAddress("one16qsd5ant9v94jrs89mruzx62h7ekcfxmduh2rx") |
||||
blsPubKey = bls.RandPrivateKey().GetPublicKey() |
||||
) |
||||
|
||||
func TestMsgCreateValidatorRLP(t *testing.T) { |
||||
commissionRate := NewDecWithPrec(1, 2) // 10%
|
||||
maxRate := NewDecWithPrec(2, 2) // 20%
|
||||
maxChangeRate := NewDecWithPrec(1, 3) // 1%
|
||||
|
||||
blsPublickey := shard.BlsPublicKey{} |
||||
blsPublickey.FromLibBLSPublicKey(blsPubKey) |
||||
|
||||
msgCreateValidator := NewMsgCreateValidator(Description{ |
||||
Name: "validator 1", |
||||
Identity: "1", |
||||
Website: "harmony.one", |
||||
SecurityContact: "11.111.1111", |
||||
Details: "the best validator ever", |
||||
}, CommissionRates{ |
||||
Rate: commissionRate, |
||||
MaxRate: maxRate, |
||||
MaxChangeRate: maxChangeRate, |
||||
}, minSelfDelegation, validatorAddress, blsPublickey, stakeAmount) |
||||
|
||||
rlpBytes, err := rlp.EncodeToBytes(msgCreateValidator) |
||||
if err != nil { |
||||
t.Error("failed to rlp encode 'create validator' message") |
||||
} |
||||
|
||||
decodedMsg := &MsgCreateValidator{} |
||||
err = rlp.DecodeBytes(rlpBytes, decodedMsg) |
||||
|
||||
if err != nil { |
||||
t.Error("failed to rlp decode 'create validator' message") |
||||
} |
||||
|
||||
if !decodedMsg.Commission.Rate.Equal(msgCreateValidator.Commission.Rate) { |
||||
t.Error("Commission rate does not match") |
||||
} |
||||
|
||||
if !decodedMsg.Commission.MaxRate.Equal(msgCreateValidator.Commission.MaxRate) { |
||||
t.Error("MaxRate does not match") |
||||
} |
||||
|
||||
if !decodedMsg.Commission.MaxChangeRate.Equal(msgCreateValidator.Commission.MaxChangeRate) { |
||||
t.Error("MaxChangeRate does not match") |
||||
} |
||||
|
||||
if !reflect.DeepEqual(decodedMsg.Description, msgCreateValidator.Description) { |
||||
t.Error("Description does not match") |
||||
} |
||||
|
||||
if decodedMsg.MinSelfDelegation.Cmp(msgCreateValidator.MinSelfDelegation) != 0 { |
||||
t.Error("MinSelfDelegation does not match") |
||||
} |
||||
|
||||
if decodedMsg.StakingAddress.Hex() != msgCreateValidator.StakingAddress.Hex() { |
||||
t.Error("StakingAddress does not match") |
||||
} |
||||
|
||||
if shard.CompareBlsPublicKey(decodedMsg.ValidatingPubKey, msgCreateValidator.ValidatingPubKey) != 0 { |
||||
t.Error("ValidatingPubKey does not match") |
||||
} |
||||
|
||||
if decodedMsg.Amount.Cmp(msgCreateValidator.Amount) != 0 { |
||||
t.Error("Amount does not match") |
||||
} |
||||
} |
||||
|
||||
func TestMsgEditValidatorRLP(t *testing.T) { |
||||
commissionRate := NewDecWithPrec(1, 2) // 10%
|
||||
|
||||
blsPublickey := shard.BlsPublicKey{} |
||||
blsPublickey.FromLibBLSPublicKey(blsPubKey) |
||||
|
||||
msgEditValidator := NewMsgEditValidator(Description{ |
||||
Name: "validator 1", |
||||
Identity: "1", |
||||
Website: "harmony.one", |
||||
SecurityContact: "11.111.1111", |
||||
Details: "the best validator ever", |
||||
}, validatorAddress, commissionRate, minSelfDelegation) |
||||
|
||||
rlpBytes, err := rlp.EncodeToBytes(msgEditValidator) |
||||
if err != nil { |
||||
t.Error("failed to rlp encode 'create validator' message") |
||||
} |
||||
|
||||
decodedMsg := &MsgEditValidator{} |
||||
err = rlp.DecodeBytes(rlpBytes, decodedMsg) |
||||
|
||||
if err != nil { |
||||
t.Error("failed to rlp decode 'create validator' message") |
||||
} |
||||
|
||||
if !reflect.DeepEqual(decodedMsg.Description, msgEditValidator.Description) { |
||||
t.Error("Description does not match") |
||||
} |
||||
|
||||
if decodedMsg.StakingAddress.Hex() != msgEditValidator.StakingAddress.Hex() { |
||||
t.Error("StakingAddress does not match") |
||||
} |
||||
|
||||
if !decodedMsg.CommissionRate.Equal(msgEditValidator.CommissionRate) { |
||||
t.Error("Commission rate does not match") |
||||
} |
||||
|
||||
if decodedMsg.MinSelfDelegation.Cmp(msgEditValidator.MinSelfDelegation) != 0 { |
||||
t.Error("MinSelfDelegation does not match") |
||||
} |
||||
} |
||||
|
||||
func TestMsgDelegateRLP(t *testing.T) { |
||||
msgDelegate := NewMsgDelegate(delegatorAddress, validatorAddress, delegateAmount) |
||||
|
||||
rlpBytes, err := rlp.EncodeToBytes(msgDelegate) |
||||
if err != nil { |
||||
t.Error("failed to rlp encode 'create validator' message") |
||||
} |
||||
|
||||
decodedMsg := &MsgDelegate{} |
||||
err = rlp.DecodeBytes(rlpBytes, decodedMsg) |
||||
|
||||
if err != nil { |
||||
t.Error("failed to rlp decode 'create validator' message") |
||||
} |
||||
|
||||
if decodedMsg.DelegatorAddress.Hex() != msgDelegate.DelegatorAddress.Hex() { |
||||
t.Error("DelegatorAddress does not match") |
||||
} |
||||
|
||||
if decodedMsg.ValidatorAddress.Hex() != msgDelegate.ValidatorAddress.Hex() { |
||||
t.Error("ValidatorAddress does not match") |
||||
} |
||||
|
||||
if decodedMsg.Amount.Cmp(msgDelegate.Amount) != 0 { |
||||
t.Error("Amount does not match") |
||||
} |
||||
} |
||||
|
||||
func TestMsgRedelegateRLP(t *testing.T) { |
||||
msgRedelegate := NewMsgRedelegate(delegatorAddress, validatorAddress, validatorAddress2, delegateAmount) |
||||
|
||||
rlpBytes, err := rlp.EncodeToBytes(msgRedelegate) |
||||
if err != nil { |
||||
t.Error("failed to rlp encode 'create validator' message") |
||||
} |
||||
|
||||
decodedMsg := &MsgRedelegate{} |
||||
err = rlp.DecodeBytes(rlpBytes, decodedMsg) |
||||
|
||||
if err != nil { |
||||
t.Error("failed to rlp decode 'create validator' message") |
||||
} |
||||
|
||||
if decodedMsg.DelegatorAddress.Hex() != msgRedelegate.DelegatorAddress.Hex() { |
||||
t.Error("DelegatorAddress does not match") |
||||
} |
||||
|
||||
if decodedMsg.ValidatorSrcAddress.Hex() != msgRedelegate.ValidatorSrcAddress.Hex() { |
||||
t.Error("ValidatorSrcAddress does not match") |
||||
} |
||||
|
||||
if decodedMsg.ValidatorDstAddress.Hex() != msgRedelegate.ValidatorDstAddress.Hex() { |
||||
t.Error("ValidatorDstAddress does not match") |
||||
} |
||||
|
||||
if decodedMsg.Amount.Cmp(msgRedelegate.Amount) != 0 { |
||||
t.Error("Amount does not match") |
||||
} |
||||
} |
||||
|
||||
func TestMsgUndelegateRLP(t *testing.T) { |
||||
msgUndelegate := NewMsgUndelegate(delegatorAddress, validatorAddress, delegateAmount) |
||||
|
||||
rlpBytes, err := rlp.EncodeToBytes(msgUndelegate) |
||||
if err != nil { |
||||
t.Error("failed to rlp encode 'create validator' message") |
||||
} |
||||
|
||||
decodedMsg := &MsgUndelegate{} |
||||
err = rlp.DecodeBytes(rlpBytes, decodedMsg) |
||||
// var (
|
||||
// minSelfDelegation = big.NewInt(1000)
|
||||
// stakeAmount = big.NewInt(2000)
|
||||
// delegateAmount = big.NewInt(500)
|
||||
// validatorAddress = common.Address(common.MustBech32ToAddress("one1pdv9lrdwl0rg5vglh4xtyrv3wjk3wsqket7zxy"))
|
||||
// validatorAddress2 = common.Address(common.MustBech32ToAddress("one1d2rngmem4x2c6zxsjjz29dlah0jzkr0k2n88wc"))
|
||||
// delegatorAddress = common.Address(common.MustBech32ToAddress("one16qsd5ant9v94jrs89mruzx62h7ekcfxmduh2rx"))
|
||||
// blsPubKey = bls.RandPrivateKey().GetPublicKey()
|
||||
// )
|
||||
|
||||
// func TestMsgCreateValidatorRLP(t *testing.T) {
|
||||
// commissionRate := NewDecWithPrec(1, 2) // 10%
|
||||
// maxRate := NewDecWithPrec(2, 2) // 20%
|
||||
// maxChangeRate := NewDecWithPrec(1, 3) // 1%
|
||||
|
||||
// blsPublickey := shard.BlsPublicKey{}
|
||||
// blsPublickey.FromLibBLSPublicKey(blsPubKey)
|
||||
|
||||
// msgCreateValidator := NewMsgCreateValidator(Description{
|
||||
// Name: "validator 1",
|
||||
// Identity: "1",
|
||||
// Website: "harmony.one",
|
||||
// SecurityContact: "11.111.1111",
|
||||
// Details: "the best validator ever",
|
||||
// }, CommissionRates{
|
||||
// Rate: commissionRate,
|
||||
// MaxRate: maxRate,
|
||||
// MaxChangeRate: maxChangeRate,
|
||||
// }, minSelfDelegation, validatorAddress, blsPublickey, stakeAmount)
|
||||
|
||||
// rlpBytes, err := rlp.EncodeToBytes(msgCreateValidator)
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp encode 'create validator' message")
|
||||
// }
|
||||
|
||||
// decodedMsg := &MsgCreateValidator{}
|
||||
// err = rlp.DecodeBytes(rlpBytes, decodedMsg)
|
||||
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp decode 'create validator' message")
|
||||
// }
|
||||
|
||||
// if !decodedMsg.Commission.Rate.Equal(msgCreateValidator.Commission.Rate) {
|
||||
// t.Error("Commission rate does not match")
|
||||
// }
|
||||
|
||||
// if !decodedMsg.Commission.MaxRate.Equal(msgCreateValidator.Commission.MaxRate) {
|
||||
// t.Error("MaxRate does not match")
|
||||
// }
|
||||
|
||||
// if !decodedMsg.Commission.MaxChangeRate.Equal(msgCreateValidator.Commission.MaxChangeRate) {
|
||||
// t.Error("MaxChangeRate does not match")
|
||||
// }
|
||||
|
||||
// if !reflect.DeepEqual(decodedMsg.Description, msgCreateValidator.Description) {
|
||||
// t.Error("Description does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.MinSelfDelegation.Cmp(msgCreateValidator.MinSelfDelegation) != 0 {
|
||||
// t.Error("MinSelfDelegation does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.StakingAddress.String() != msgCreateValidator.StakingAddress.String() {
|
||||
// t.Error("StakingAddress does not match")
|
||||
// }
|
||||
|
||||
// if shard.CompareBlsPublicKey(decodedMsg.ValidatingPubKey, msgCreateValidator.ValidatingPubKey) != 0 {
|
||||
// t.Error("ValidatingPubKey does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.Amount.Cmp(msgCreateValidator.Amount) != 0 {
|
||||
// t.Error("Amount does not match")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestMsgEditValidatorRLP(t *testing.T) {
|
||||
// commissionRate := NewDecWithPrec(1, 2) // 10%
|
||||
|
||||
// blsPublickey := shard.BlsPublicKey{}
|
||||
// blsPublickey.FromLibBLSPublicKey(blsPubKey)
|
||||
|
||||
// msgEditValidator := NewMsgEditValidator(Description{
|
||||
// Name: "validator 1",
|
||||
// Identity: "1",
|
||||
// Website: "harmony.one",
|
||||
// SecurityContact: "11.111.1111",
|
||||
// Details: "the best validator ever",
|
||||
// }, validatorAddress, commissionRate, minSelfDelegation)
|
||||
|
||||
// rlpBytes, err := rlp.EncodeToBytes(msgEditValidator)
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp encode 'create validator' message")
|
||||
// }
|
||||
|
||||
// decodedMsg := &MsgEditValidator{}
|
||||
// err = rlp.DecodeBytes(rlpBytes, decodedMsg)
|
||||
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp decode 'create validator' message")
|
||||
// }
|
||||
|
||||
// if !reflect.DeepEqual(decodedMsg.Description, msgEditValidator.Description) {
|
||||
// t.Error("Description does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.StakingAddress.String() != msgEditValidator.StakingAddress.String() {
|
||||
// t.Error("StakingAddress does not match")
|
||||
// }
|
||||
|
||||
// if !decodedMsg.CommissionRate.Equal(msgEditValidator.CommissionRate) {
|
||||
// t.Error("Commission rate does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.MinSelfDelegation.Cmp(msgEditValidator.MinSelfDelegation) != 0 {
|
||||
// t.Error("MinSelfDelegation does not match")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestMsgDelegateRLP(t *testing.T) {
|
||||
// msgDelegate := NewMsgDelegate(delegatorAddress, validatorAddress, delegateAmount)
|
||||
|
||||
// rlpBytes, err := rlp.EncodeToBytes(msgDelegate)
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp encode 'create validator' message")
|
||||
// }
|
||||
|
||||
// decodedMsg := &MsgDelegate{}
|
||||
// err = rlp.DecodeBytes(rlpBytes, decodedMsg)
|
||||
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp decode 'create validator' message")
|
||||
// }
|
||||
|
||||
// if decodedMsg.DelegatorAddress.String() != msgDelegate.DelegatorAddress.String() {
|
||||
// t.Error("DelegatorAddress does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.ValidatorAddress.String() != msgDelegate.ValidatorAddress.String() {
|
||||
// t.Error("ValidatorAddress does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.Amount.Cmp(msgDelegate.Amount) != 0 {
|
||||
// t.Error("Amount does not match")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestMsgRedelegateRLP(t *testing.T) {
|
||||
// msgRedelegate := NewMsgRedelegate(delegatorAddress, validatorAddress, validatorAddress2, delegateAmount)
|
||||
|
||||
// rlpBytes, err := rlp.EncodeToBytes(msgRedelegate)
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp encode 'create validator' message")
|
||||
// }
|
||||
|
||||
// decodedMsg := &MsgRedelegate{}
|
||||
// err = rlp.DecodeBytes(rlpBytes, decodedMsg)
|
||||
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp decode 'create validator' message")
|
||||
// }
|
||||
|
||||
// if decodedMsg.DelegatorAddress.String() != msgRedelegate.DelegatorAddress.String() {
|
||||
// t.Error("DelegatorAddress does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.ValidatorSrcAddress.String() != msgRedelegate.ValidatorSrcAddress.String() {
|
||||
// t.Error("ValidatorSrcAddress does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.ValidatorDstAddress.String() != msgRedelegate.ValidatorDstAddress.String() {
|
||||
// t.Error("ValidatorDstAddress does not match")
|
||||
// }
|
||||
|
||||
// if decodedMsg.Amount.Cmp(msgRedelegate.Amount) != 0 {
|
||||
// t.Error("Amount does not match")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestMsgUndelegateRLP(t *testing.T) {
|
||||
// msgUndelegate := NewMsgUndelegate(delegatorAddress, validatorAddress, delegateAmount)
|
||||
|
||||
// rlpBytes, err := rlp.EncodeToBytes(msgUndelegate)
|
||||
// if err != nil {
|
||||
// t.Error("failed to rlp encode 'create validator' message")
|
||||
// }
|
||||
|
||||
// decodedMsg := &MsgUndelegate{}
|
||||
// err = rlp.DecodeBytes(rlpBytes, decodedMsg)
|
||||
|
||||
if err != nil { |
||||
t.Error("failed to rlp decode 'create validator' message") |
||||
} |
||||
// if err != nil {
|
||||
// t.Error("failed to rlp decode 'create validator' message")
|
||||
// }
|
||||
|
||||
if decodedMsg.DelegatorAddress.Hex() != msgUndelegate.DelegatorAddress.Hex() { |
||||
t.Error("DelegatorAddress does not match") |
||||
} |
||||
// if decodedMsg.DelegatorAddress.String() != msgUndelegate.DelegatorAddress.String() {
|
||||
// t.Error("DelegatorAddress does not match")
|
||||
// }
|
||||
|
||||
if decodedMsg.ValidatorAddress.Hex() != msgUndelegate.ValidatorAddress.Hex() { |
||||
t.Error("ValidatorAddress does not match") |
||||
} |
||||
// if decodedMsg.ValidatorAddress.String() != msgUndelegate.ValidatorAddress.String() {
|
||||
// t.Error("ValidatorAddress does not match")
|
||||
// }
|
||||
|
||||
if decodedMsg.Amount.Cmp(msgUndelegate.Amount) != 0 { |
||||
t.Error("Amount does not match") |
||||
} |
||||
} |
||||
// if decodedMsg.Amount.Cmp(msgUndelegate.Amount) != 0 {
|
||||
// t.Error("Amount does not match")
|
||||
// }
|
||||
// }
|
||||
|
@ -0,0 +1,199 @@ |
||||
// 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 types |
||||
|
||||
import ( |
||||
"crypto/ecdsa" |
||||
"errors" |
||||
"fmt" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/harmony-one/harmony/core" |
||||
"github.com/harmony-one/harmony/crypto/hash" |
||||
) |
||||
|
||||
// sigCache is used to cache the derived sender and contains
|
||||
// the signer used to derive it.
|
||||
type sigCache struct { |
||||
signer Signer |
||||
from common.Address |
||||
} |
||||
|
||||
// Sign signs the stake using the given signer and private key
|
||||
func Sign(tx *StakingTransaction, s Signer, prv *ecdsa.PrivateKey) (*StakingTransaction, error) { |
||||
h := s.Hash(tx) |
||||
sig, err := crypto.Sign(h[:], prv) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return tx.WithSignature(s, sig) |
||||
} |
||||
|
||||
// Sender returns the address derived from the signature (V, R, S) using secp256k1
|
||||
// elliptic curve and an error if it failed deriving or upon an incorrect
|
||||
// signature.
|
||||
//
|
||||
// Sender may cache the address, allowing it to be used regardless of
|
||||
// signing method. The cache is invalidated if the cached signer does
|
||||
// not match the signer used in the current call.
|
||||
func Sender(signer Signer, tx *StakingTransaction) (common.Address, error) { |
||||
if sc := tx.from.Load(); sc != nil { |
||||
sigCache := sc.(sigCache) |
||||
// If the signer used to derive from in a previous
|
||||
// call is not the same as used current, invalidate
|
||||
// the cache.
|
||||
if sigCache.signer.Equal(signer) { |
||||
return sigCache.from, nil |
||||
} |
||||
} |
||||
addr, err := signer.Sender(tx) |
||||
if err != nil { |
||||
return common.Address{}, err |
||||
} |
||||
tx.from.Store(sigCache{signer: signer, from: addr}) |
||||
return addr, nil |
||||
} |
||||
|
||||
// Signer encapsulates transaction signature handling. Note that this interface is not a
|
||||
// stable API and may change at any time to accommodate new protocol rules.
|
||||
type Signer interface { |
||||
// Sender returns the sender address of the transaction.
|
||||
Sender(tx *StakingTransaction) (common.Address, error) |
||||
// SignatureValues returns the raw R, S, V values corresponding to the
|
||||
// given signature.
|
||||
SignatureValues(tx *StakingTransaction, sig []byte) (r, s, v *big.Int, err error) |
||||
// Hash returns the hash to be signed.
|
||||
Hash(tx *StakingTransaction) common.Hash |
||||
// Equal returns true if the given signer is the same as the receiver.
|
||||
Equal(s Signer) bool |
||||
} |
||||
|
||||
// EIP155Signer implements Signer using the EIP155 rules.
|
||||
type EIP155Signer struct { |
||||
chainID, chainIDMul *big.Int |
||||
} |
||||
|
||||
// NewEIP155Signer creates a EIP155Signer given chainID.
|
||||
func NewEIP155Signer(chainID *big.Int) EIP155Signer { |
||||
if chainID == nil { |
||||
chainID = new(big.Int) |
||||
} |
||||
return EIP155Signer{ |
||||
chainID: chainID, |
||||
chainIDMul: new(big.Int).Mul(chainID, big.NewInt(2)), |
||||
} |
||||
} |
||||
|
||||
// Equal checks if the given EIP155Signer is equal to another Signer.
|
||||
func (s EIP155Signer) Equal(s2 Signer) bool { |
||||
eip155, ok := s2.(EIP155Signer) |
||||
return ok && eip155.chainID.Cmp(s.chainID) == 0 |
||||
} |
||||
|
||||
var big8 = big.NewInt(8) |
||||
|
||||
// Sender returns the sender address of the given signer.
|
||||
func (s EIP155Signer) Sender(tx *StakingTransaction) (common.Address, error) { |
||||
if tx.ChainID().Cmp(s.chainID) != 0 { |
||||
return common.Address{}, core.ErrInvalidChainID |
||||
} |
||||
V := new(big.Int).Sub(tx.data.V, s.chainIDMul) |
||||
V.Sub(V, big8) |
||||
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true) |
||||
} |
||||
|
||||
// SignatureValues returns signature values. This signature
|
||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (s EIP155Signer) SignatureValues( |
||||
tx *StakingTransaction, sig []byte, |
||||
) (R, S, V *big.Int, err error) { |
||||
sigValues := func(tx *StakingTransaction, sig []byte) (r, s, v *big.Int, err error) { |
||||
if len(sig) != 65 { |
||||
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) |
||||
} |
||||
r = new(big.Int).SetBytes(sig[:32]) |
||||
s = new(big.Int).SetBytes(sig[32:64]) |
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 27}) |
||||
return r, s, v, nil |
||||
} |
||||
R, S, V, err = sigValues(tx, sig) |
||||
if err != nil { |
||||
return nil, nil, nil, err |
||||
} |
||||
if s.chainID.Sign() != 0 { |
||||
V = big.NewInt(int64(sig[64] + 35)) |
||||
V.Add(V, s.chainIDMul) |
||||
} |
||||
return R, S, V, nil |
||||
} |
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s EIP155Signer) Hash(tx *StakingTransaction) common.Hash { |
||||
return hash.FromRLP([]interface{}{ |
||||
tx.data.Directive, |
||||
tx.data.StakeMsg, |
||||
tx.data.AccountNonce, |
||||
tx.data.Price, |
||||
tx.data.GasLimit, |
||||
s.chainID, uint(0), uint(0), |
||||
}) |
||||
} |
||||
|
||||
func recoverPlain( |
||||
sighash common.Hash, R, S, Vb *big.Int, homestead bool, |
||||
) (common.Address, error) { |
||||
if Vb.BitLen() > 8 { |
||||
return common.Address{}, ErrInvalidSig |
||||
} |
||||
V := byte(Vb.Uint64() - 27) |
||||
if !crypto.ValidateSignatureValues(V, R, S, homestead) { |
||||
return common.Address{}, ErrInvalidSig |
||||
} |
||||
// encode the signature in uncompressed format
|
||||
r, s := R.Bytes(), S.Bytes() |
||||
sig := make([]byte, 65) |
||||
copy(sig[32-len(r):32], r) |
||||
copy(sig[64-len(s):64], s) |
||||
sig[64] = V |
||||
// recover the public key from the signature
|
||||
pub, err := crypto.Ecrecover(sighash[:], sig) |
||||
if err != nil { |
||||
return common.Address{}, err |
||||
} |
||||
if len(pub) == 0 || pub[0] != 4 { |
||||
return common.Address{}, errors.New("invalid public key") |
||||
} |
||||
var addr common.Address |
||||
copy(addr[:], crypto.Keccak256(pub[1:])[12:]) |
||||
return addr, nil |
||||
} |
||||
|
||||
// deriveChainID derives the chain id from the given v parameter
|
||||
func deriveChainID(v *big.Int) *big.Int { |
||||
if v.BitLen() <= 64 { |
||||
v := v.Uint64() |
||||
if v == 27 || v == 28 { |
||||
return new(big.Int) |
||||
} |
||||
return new(big.Int).SetUint64((v - 35) / 2) |
||||
} |
||||
v = new(big.Int).Sub(v, big.NewInt(35)) |
||||
return v.Div(v, big.NewInt(2)) |
||||
} |
@ -1,39 +0,0 @@ |
||||
package types |
||||
|
||||
import ( |
||||
"bytes" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/harmony-one/harmony/crypto/hash" |
||||
) |
||||
|
||||
// StakingTransaction struct.
|
||||
type StakingTransaction struct { |
||||
AccountNonce uint64 `json:"nonce" gencodec:"required"` |
||||
Price *big.Int `json:"gasPrice" gencodec:"required"` |
||||
GasLimit uint64 `json:"gas" gencodec:"required"` |
||||
Msg StakingMessage `json:"msg" gencodec:"required"` |
||||
|
||||
// Signature values
|
||||
V *big.Int `json:"v" gencodec:"required"` |
||||
R *big.Int `json:"r" gencodec:"required"` |
||||
S *big.Int `json:"s" gencodec:"required"` |
||||
|
||||
// This is only used when marshaling to JSON.
|
||||
hash *common.Hash `json:"hash" rlp:"-"` |
||||
} |
||||
|
||||
// StakingTransactions is a Transaction slice type for basic sorting.
|
||||
type StakingTransactions []*StakingTransaction |
||||
|
||||
// Hash hashes the RLP encoding of tx.
|
||||
// It uniquely identifies the transaction.
|
||||
func (tx *StakingTransaction) Hash() common.Hash { |
||||
emptyHash := common.Hash{} |
||||
if bytes.Compare(tx.hash[:], emptyHash[:]) == 0 { |
||||
h := hash.FromRLP(tx) |
||||
tx.hash = &h |
||||
} |
||||
return *tx.hash |
||||
} |
@ -0,0 +1,110 @@ |
||||
package types |
||||
|
||||
import ( |
||||
"errors" |
||||
"io" |
||||
"math/big" |
||||
"sync/atomic" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/harmony-one/harmony/crypto/hash" |
||||
) |
||||
|
||||
type txdata struct { |
||||
Directive |
||||
StakeMsg interface{} |
||||
AccountNonce uint64 `json:"nonce" gencodec:"required"` |
||||
Price *big.Int `json:"gasPrice" gencodec:"required"` |
||||
GasLimit uint64 `json:"gas" gencodec:"required"` |
||||
// Signature values
|
||||
V *big.Int `json:"v" gencodec:"required"` |
||||
R *big.Int `json:"r" gencodec:"required"` |
||||
S *big.Int `json:"s" gencodec:"required"` |
||||
// This is only used when marshaling to JSON.
|
||||
Hash *common.Hash `json:"hash" rlp:"-"` |
||||
} |
||||
|
||||
// StakingTransaction is a record captuing all staking operations
|
||||
type StakingTransaction struct { |
||||
data txdata |
||||
// caches
|
||||
hash atomic.Value |
||||
size atomic.Value |
||||
from atomic.Value |
||||
} |
||||
|
||||
type fulfill func() (Directive, interface{}) |
||||
|
||||
// NewStakingTransaction produces a new staking transaction record
|
||||
func NewStakingTransaction( |
||||
nonce, gasLimit uint64, gasPrice *big.Int, f fulfill, |
||||
) (*StakingTransaction, error) { |
||||
directive, payload := f() |
||||
// TODO(Double check that this is legitmate directive)
|
||||
newStake := &StakingTransaction{data: txdata{ |
||||
directive, |
||||
payload, |
||||
nonce, |
||||
big.NewInt(0).Set(gasPrice), |
||||
gasLimit, |
||||
big.NewInt(0), |
||||
big.NewInt(0), |
||||
big.NewInt(0), |
||||
nil, |
||||
}} |
||||
return newStake, nil |
||||
} |
||||
|
||||
var ( |
||||
// ErrInvalidSig is a bad signature
|
||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values") |
||||
) |
||||
|
||||
// StakingTransactions is a stake slice type for basic sorting.
|
||||
type StakingTransactions []*StakingTransaction |
||||
|
||||
// Hash hashes the RLP encoding of tx.
|
||||
// It uniquely identifies the transaction.
|
||||
func (tx *StakingTransaction) Hash() common.Hash { |
||||
if hash := tx.hash.Load(); hash != nil { |
||||
return hash.(common.Hash) |
||||
} |
||||
v := hash.FromRLP(tx) |
||||
tx.hash.Store(v) |
||||
return v |
||||
} |
||||
|
||||
// WithSignature returns a new transaction with the given signature.
|
||||
func (tx *StakingTransaction) WithSignature(signer Signer, sig []byte) (*StakingTransaction, error) { |
||||
r, s, v, err := signer.SignatureValues(tx, sig) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
cpy := &StakingTransaction{data: tx.data} |
||||
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v |
||||
return cpy, nil |
||||
} |
||||
|
||||
// ChainID is what chain this staking transaction for
|
||||
func (tx *StakingTransaction) ChainID() *big.Int { |
||||
return deriveChainID(tx.data.V) |
||||
} |
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *StakingTransaction) EncodeRLP(w io.Writer) error { |
||||
return rlp.Encode(w, &tx.data) |
||||
} |
||||
|
||||
// DecodeRLP implements rlp.Decoder
|
||||
func (tx *StakingTransaction) DecodeRLP(s *rlp.Stream) error { |
||||
_, size, _ := s.Kind() |
||||
err := s.Decode(&tx.data) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if err == nil { |
||||
tx.size.Store(common.StorageSize(rlp.ListSize(size))) |
||||
} |
||||
return err |
||||
} |
Loading…
Reference in new issue