Пример #1
0
// Returns whether +2/3 have prevoted/committed for BlockHash.
func (pol *POL) Verify(valSet *sm.ValidatorSet) error {

	if uint(len(pol.Votes)) != valSet.Size() {
		return fmt.Errorf("Invalid POL votes count: Expected %v, got %v",
			valSet.Size(), len(pol.Votes))
	}

	talliedVotingPower := uint64(0)
	prevoteDoc := account.SignBytes(&types.Vote{
		Height: pol.Height, Round: pol.Round, Type: types.VoteTypePrevote,
		BlockHash:  pol.BlockHash,
		BlockParts: pol.BlockParts,
	})
	seenValidators := map[string]struct{}{}

	for idx, vote := range pol.Votes {
		// vote may be zero, in which case skip.
		if vote.Signature.IsZero() {
			continue
		}
		voteDoc := prevoteDoc
		_, val := valSet.GetByIndex(uint(idx))

		// Commit vote?
		if vote.Round < pol.Round {
			voteDoc = account.SignBytes(&types.Vote{
				Height: pol.Height, Round: vote.Round, Type: types.VoteTypeCommit,
				BlockHash:  pol.BlockHash,
				BlockParts: pol.BlockParts,
			})
		} else if vote.Round > pol.Round {
			return fmt.Errorf("Invalid commit round %v for POL %v", vote.Round, pol)
		}

		// Validate
		if _, seen := seenValidators[string(val.Address)]; seen {
			return fmt.Errorf("Duplicate validator for vote %v for POL %v", vote, pol)
		}

		if !val.PubKey.VerifyBytes(voteDoc, vote.Signature) {
			return fmt.Errorf("Invalid signature for vote %v for POL %v", vote, pol)
		}

		// Tally
		seenValidators[string(val.Address)] = struct{}{}
		talliedVotingPower += val.VotingPower
	}

	if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
		return nil
	} else {
		return fmt.Errorf("Invalid POL, insufficient voting power %v, needed %v",
			talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
	}

}
Пример #2
0
func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()

	// Already have one
	if cs.Proposal != nil {
		return nil
	}

	// Does not apply
	if proposal.Height != cs.Height || proposal.Round != cs.Round {
		return nil
	}

	// We don't care about the proposal if we're already in RoundStepCommit.
	if cs.Step == RoundStepCommit {
		return nil
	}

	// Verify signature
	if !cs.Validators.Proposer().PubKey.VerifyBytes(account.SignBytes(proposal), proposal.Signature) {
		return ErrInvalidProposalSignature
	}

	cs.Proposal = proposal
	cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockParts)
	cs.ProposalPOLParts = types.NewPartSetFromHeader(proposal.POLParts)
	return nil
}
Пример #3
0
func (cs *ConsensusState) SetProposal(proposal *types.Proposal) error {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()

	// Already have one
	if cs.Proposal != nil {
		return nil
	}

	// Does not apply
	if proposal.Height != cs.Height || proposal.Round != cs.Round {
		return nil
	}

	// We don't care about the proposal if we're already in RoundStepCommit.
	if RoundStepCommit <= cs.Step {
		return nil
	}

	// Verify POLRound, which must be -1 or between 0 and proposal.Round exclusive.
	if proposal.POLRound != -1 &&
		(proposal.POLRound < 0 || proposal.Round <= proposal.POLRound) {
		return ErrInvalidProposalPOLRound
	}

	// Verify signature
	if !cs.Validators.Proposer().PubKey.VerifyBytes(acm.SignBytes(cs.state.ChainID, proposal), proposal.Signature) {
		return ErrInvalidProposalSignature
	}

	cs.Proposal = proposal
	cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockPartsHeader)
	return nil
}
Пример #4
0
// True if added, false if not.
// Returns ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
// NOTE: vote should not be mutated after adding.
// Returns the validator index of the vote unless error is set.
func (voteSet *VoteSet) Add(address []byte, vote *types.Vote) (bool, uint, error) {
	voteSet.mtx.Lock()
	defer voteSet.mtx.Unlock()

	// Make sure the step matches. (or that vote is commit && round < voteSet.round)
	if vote.Height != voteSet.height ||
		(vote.Type != types.VoteTypeCommit && vote.Round != voteSet.round) ||
		(vote.Type != types.VoteTypeCommit && vote.Type != voteSet.type_) ||
		(vote.Type == types.VoteTypeCommit && voteSet.type_ != types.VoteTypeCommit && vote.Round >= voteSet.round) {
		return false, 0, types.ErrVoteUnexpectedStep
	}

	// Ensure that signer is a validator.
	valIndex, val := voteSet.valSet.GetByAddress(address)
	if val == nil {
		return false, 0, types.ErrVoteInvalidAccount
	}

	// Check signature.
	if !val.PubKey.VerifyBytes(account.SignBytes(vote), vote.Signature) {
		// Bad signature.
		return false, 0, types.ErrVoteInvalidSignature
	}

	return voteSet.addVote(valIndex, vote)
}
Пример #5
0
func TestSendTxSignable(t *testing.T) {
	sendTx := &SendTx{
		Inputs: []*TxInput{
			&TxInput{
				Address:  []byte("input1"),
				Amount:   12345,
				Sequence: 67890,
			},
			&TxInput{
				Address:  []byte("input2"),
				Amount:   111,
				Sequence: 222,
			},
		},
		Outputs: []*TxOutput{
			&TxOutput{
				Address: []byte("output1"),
				Amount:  333,
			},
			&TxOutput{
				Address: []byte("output2"),
				Amount:  444,
			},
		},
	}
	signBytes := account.SignBytes(sendTx)
	signStr := string(signBytes)
	expected := Fmt(`{"network":"%X","tx":[1,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"outputs":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
		config.GetString("network"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}
Пример #6
0
func TestBondTxSignable(t *testing.T) {
	privAccount := account.GenPrivAccountFromKey([64]byte{})
	bondTx := &BondTx{
		PubKey: privAccount.PubKey.(account.PubKeyEd25519),
		Inputs: []*TxInput{
			&TxInput{
				Address:  []byte("input1"),
				Amount:   12345,
				Sequence: 67890,
			},
			&TxInput{
				Address:  []byte("input2"),
				Amount:   111,
				Sequence: 222,
			},
		},
		UnbondTo: []*TxOutput{
			&TxOutput{
				Address: []byte("output1"),
				Amount:  333,
			},
			&TxOutput{
				Address: []byte("output2"),
				Amount:  444,
			},
		},
	}
	signBytes := account.SignBytes(bondTx)
	signStr := string(signBytes)
	expected := Fmt(`{"network":"%X","tx":[17,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"pub_key":[1,"3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"],"unbond_to":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
		config.GetString("network"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for BondTx")
	}
}
Пример #7
0
func (data *Data) Hash() []byte {
	if data.hash == nil {
		bs := make([]interface{}, len(data.Txs))
		for i, tx := range data.Txs {
			bs[i] = acm.SignBytes(config.GetString("chain_id"), tx)
		}
		data.hash = merkle.SimpleHashFromBinaries(bs) // NOTE: leaves are TxIDs.
	}
	return data.hash
}
Пример #8
0
func (data *Data) Hash() []byte {
	if data.hash == nil {
		bs := make([]interface{}, len(data.Txs))
		for i, tx := range data.Txs {
			bs[i] = account.SignBytes(tx)
		}
		data.hash = merkle.HashFromBinaries(bs)
	}
	return data.hash
}
Пример #9
0
func TestRebondTxSignable(t *testing.T) {
	rebondTx := &RebondTx{
		Address: []byte("address1"),
		Height:  111,
	}
	signBytes := account.SignBytes(rebondTx)
	signStr := string(signBytes)
	expected := Fmt(`{"network":"%X","tx":[19,{"address":"6164647265737331","height":111}]}`,
		config.GetString("network"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for RebondTx")
	}
}
Пример #10
0
func TestUnbondTxSignable(t *testing.T) {
	unbondTx := &UnbondTx{
		Address: []byte("address1"),
		Height:  111,
	}
	signBytes := acm.SignBytes(chainID, unbondTx)
	signStr := string(signBytes)
	expected := Fmt(`{"chain_id":"%s","tx":[18,{"address":"6164647265737331","height":111}]}`,
		config.GetString("chain_id"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for UnbondTx")
	}
}
Пример #11
0
// Verify that +2/3 of the set had signed the given signBytes
func (valSet *ValidatorSet) VerifyValidation(chainID string,
	hash []byte, parts PartSetHeader, height int, v *Validation) error {
	if valSet.Size() != len(v.Precommits) {
		return fmt.Errorf("Invalid validation -- wrong set size: %v vs %v", valSet.Size(), len(v.Precommits))
	}
	if height != v.Height() {
		return fmt.Errorf("Invalid validation -- wrong height: %v vs %v", height, v.Height())
	}

	talliedVotingPower := int64(0)
	round := v.Round()

	for idx, precommit := range v.Precommits {
		// may be nil if validator skipped.
		if precommit == nil {
			continue
		}
		if precommit.Height != height {
			return fmt.Errorf("Invalid validation -- wrong height: %v vs %v", height, precommit.Height)
		}
		if precommit.Round != round {
			return fmt.Errorf("Invalid validation -- wrong round: %v vs %v", round, precommit.Round)
		}
		if precommit.Type != VoteTypePrecommit {
			return fmt.Errorf("Invalid validation -- not precommit @ index %v", idx)
		}
		_, val := valSet.GetByIndex(idx)
		// Validate signature
		precommitSignBytes := account.SignBytes(chainID, precommit)
		if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
			return fmt.Errorf("Invalid validation -- invalid signature: %v", precommit)
		}
		if !bytes.Equal(precommit.BlockHash, hash) {
			continue // Not an error, but doesn't count
		}
		if !parts.Equals(precommit.BlockPartsHeader) {
			continue // Not an error, but doesn't count
		}
		// Good precommit!
		talliedVotingPower += val.VotingPower
	}

	if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
		return nil
	} else {
		return fmt.Errorf("Invalid validation -- insufficient voting power: got %v, needed %v",
			talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
	}
}
Пример #12
0
func TestProposalSignable(t *testing.T) {
	proposal := &Proposal{
		Height:     12345,
		Round:      23456,
		BlockParts: types.PartSetHeader{111, []byte("blockparts")},
		POLParts:   types.PartSetHeader{222, []byte("polparts")},
		Signature:  nil,
	}
	signBytes := account.SignBytes(proposal)
	signStr := string(signBytes)
	expected := Fmt(`{"network":"%X","proposal":{"block_parts":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_parts":{"hash":"706F6C7061727473","total":222},"round":23456}}`,
		config.GetString("network"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}
Пример #13
0
func TestProposalSignable(t *testing.T) {
	proposal := &Proposal{
		Height:           12345,
		Round:            23456,
		BlockPartsHeader: PartSetHeader{111, []byte("blockparts")},
		POLRound:         -1,
	}
	signBytes := acm.SignBytes(config.GetString("chain_id"), proposal)
	signStr := string(signBytes)

	expected := Fmt(`{"chain_id":"%s","proposal":{"block_parts_header":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_round":-1,"round":23456}}`,
		config.GetString("chain_id"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}
Пример #14
0
func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool, int, error) {

	// Make sure the step matches. (or that vote is commit && round < voteSet.round)
	if (vote.Height != voteSet.height) ||
		(vote.Round != voteSet.round) ||
		(vote.Type != voteSet.type_) {
		return false, 0, ErrVoteUnexpectedStep
	}

	// Check signature.
	if !val.PubKey.VerifyBytes(acm.SignBytes(config.GetString("chain_id"), vote), vote.Signature) {
		// Bad signature.
		return false, 0, ErrVoteInvalidSignature
	}

	// If vote already exists, return false.
	if existingVote := voteSet.votes[valIndex]; existingVote != nil {
		if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
			return false, valIndex, nil
		} else {
			return false, valIndex, &ErrVoteConflictingSignature{
				VoteA: existingVote,
				VoteB: vote,
			}
		}
	}

	// Add vote.
	voteSet.votes[valIndex] = vote
	voteSet.votesBitArray.SetIndex(valIndex, true)
	blockKey := string(vote.BlockHash) + string(wire.BinaryBytes(vote.BlockPartsHeader))
	totalBlockHashVotes := voteSet.votesByBlock[blockKey] + val.VotingPower
	voteSet.votesByBlock[blockKey] = totalBlockHashVotes
	voteSet.totalVotes += val.VotingPower

	// If we just nudged it up to two thirds majority, add it.
	if totalBlockHashVotes > voteSet.valSet.TotalVotingPower()*2/3 &&
		(totalBlockHashVotes-val.VotingPower) <= voteSet.valSet.TotalVotingPower()*2/3 {
		voteSet.maj23Hash = vote.BlockHash
		voteSet.maj23PartsHeader = vote.BlockPartsHeader
		voteSet.maj23Exists = true
	}

	return true, valIndex, nil
}
Пример #15
0
func (privVal *PrivValidator) SignRebondTx(rebondTx *types.RebondTx) error {
	privVal.mtx.Lock()
	defer privVal.mtx.Unlock()
	if privVal.LastHeight < rebondTx.Height {

		// Persist height/round/step
		privVal.LastHeight = rebondTx.Height
		privVal.LastRound = math.MaxUint64 // We can't do anything else for this rebondTx.Height.
		privVal.LastStep = math.MaxUint8
		privVal.save()

		// Sign
		rebondTx.Signature = privVal.PrivKey.Sign(account.SignBytes(rebondTx)).(account.SignatureEd25519)
		return nil
	} else {
		return errors.New(fmt.Sprintf("Attempt of duplicate signing of rebondTx: Height %v", rebondTx.Height))
	}
}
Пример #16
0
func checkTx(t *testing.T, fromAddr []byte, priv *acm.PrivAccount, tx *types.SendTx) {
	if bytes.Compare(tx.Inputs[0].Address, fromAddr) != 0 {
		t.Fatal("Tx input addresses don't match!")
	}

	signBytes := acm.SignBytes(chainID, tx)
	in := tx.Inputs[0] //(*types.SendTx).Inputs[0]

	if err := in.ValidateBasic(); err != nil {
		t.Fatal(err)
	}
	// Check signatures
	// acc := getAccount(t, byteAddr)
	// NOTE: using the acc here instead of the in fails; it is nil.
	if !in.PubKey.VerifyBytes(signBytes, in.Signature) {
		t.Fatal(types.ErrTxInvalidSignature)
	}
}
Пример #17
0
func TestNameTxSignable(t *testing.T) {
	nameTx := &NameTx{
		Input: &TxInput{
			Address:  []byte("input1"),
			Amount:   12345,
			Sequence: 250,
		},
		Name: "google.com",
		Data: "secretly.not.google.com",
		Fee:  1000,
	}
	signBytes := acm.SignBytes(chainID, nameTx)
	signStr := string(signBytes)
	expected := Fmt(`{"chain_id":"%s","tx":[3,{"data":"secretly.not.google.com","fee":1000,"input":{"address":"696E70757431","amount":12345,"sequence":250},"name":"google.com"}]}`,
		config.GetString("chain_id"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}
Пример #18
0
func (privVal *PrivValidator) SignRebondTx(chainID string, rebondTx *RebondTx) error {
	privVal.mtx.Lock()
	defer privVal.mtx.Unlock()
	if privVal.LastHeight < rebondTx.Height {

		// Persist height/round/step
		// Prevent doing anything else for this rebondTx.Height.
		privVal.LastHeight = rebondTx.Height
		privVal.LastRound = math.MaxInt32 // MaxInt64 overflows on 32bit architectures.
		privVal.LastStep = math.MaxInt8
		privVal.save()

		// Sign
		rebondTx.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, rebondTx)).(acm.SignatureEd25519)
		return nil
	} else {
		return errors.New(fmt.Sprintf("Attempt of duplicate signing of rebondTx: Height %v", rebondTx.Height))
	}
}
Пример #19
0
// Verify that +2/3 of the set had signed the given signBytes
func (valSet *ValidatorSet) VerifyValidation(hash []byte, parts types.PartSetHeader, height uint, v *types.Validation) error {
	if valSet.Size() != uint(len(v.Commits)) {
		return errors.New(Fmt("Invalid validation -- wrong set size: %v vs %v",
			valSet.Size(), len(v.Commits)))
	}

	talliedVotingPower := uint64(0)
	seenValidators := map[string]struct{}{}

	for idx, commit := range v.Commits {
		// may be zero, in which case skip.
		if commit.Signature.IsZero() {
			continue
		}
		_, val := valSet.GetByIndex(uint(idx))
		commitSignBytes := account.SignBytes(&types.Vote{
			Height: height, Round: commit.Round, Type: types.VoteTypeCommit,
			BlockHash:  hash,
			BlockParts: parts,
		})

		// Validate
		if _, seen := seenValidators[string(val.Address)]; seen {
			return fmt.Errorf("Duplicate validator for commit %v for Validation %v", commit, v)
		}

		if !val.PubKey.VerifyBytes(commitSignBytes, commit.Signature) {
			return fmt.Errorf("Invalid signature for commit %v for Validation %v", commit, v)
		}

		// Tally
		seenValidators[string(val.Address)] = struct{}{}
		talliedVotingPower += val.VotingPower
	}

	if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
		return nil
	} else {
		return fmt.Errorf("insufficient voting power %v, needed %v",
			talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
	}
}
Пример #20
0
func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error {
	privVal.mtx.Lock()
	defer privVal.mtx.Unlock()
	if privVal.LastHeight < proposal.Height ||
		privVal.LastHeight == proposal.Height && privVal.LastRound < proposal.Round ||
		privVal.LastHeight == 0 && privVal.LastRound == 0 && privVal.LastStep == stepNone {

		// Persist height/round/step
		privVal.LastHeight = proposal.Height
		privVal.LastRound = proposal.Round
		privVal.LastStep = stepPropose
		privVal.save()

		// Sign
		proposal.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, proposal)).(acm.SignatureEd25519)
		return nil
	} else {
		return errors.New(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v", proposal.Height, proposal.Round))
	}
}
Пример #21
0
func TestCallTxSignable(t *testing.T) {
	callTx := &CallTx{
		Input: &TxInput{
			Address:  []byte("input1"),
			Amount:   12345,
			Sequence: 67890,
		},
		Address:  []byte("contract1"),
		GasLimit: 111,
		Fee:      222,
		Data:     []byte("data1"),
	}
	signBytes := account.SignBytes(callTx)
	signStr := string(signBytes)
	expected := Fmt(`{"network":"%X","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`,
		config.GetString("network"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}
Пример #22
0
func TestPermissionsTxSignable(t *testing.T) {
	permsTx := &PermissionsTx{
		Input: &TxInput{
			Address:  []byte("input1"),
			Amount:   12345,
			Sequence: 250,
		},
		PermArgs: &ptypes.SetBaseArgs{
			Address:    []byte("address1"),
			Permission: 1,
			Value:      true,
		},
	}
	signBytes := acm.SignBytes(chainID, permsTx)
	signStr := string(signBytes)
	expected := Fmt(`{"chain_id":"%s","tx":[32,{"args":"[2,{"address":"6164647265737331","permission":1,"value":true}]","input":{"address":"696E70757431","amount":12345,"sequence":250}}]}`,
		config.GetString("chain_id"))
	if signStr != expected {
		t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}
Пример #23
0
func TestBondTxSignable(t *testing.T) {
	privKeyBytes := make([]byte, 64)
	privAccount := acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes)
	bondTx := &BondTx{
		PubKey: privAccount.PubKey.(acm.PubKeyEd25519),
		Inputs: []*TxInput{
			&TxInput{
				Address:  []byte("input1"),
				Amount:   12345,
				Sequence: 67890,
			},
			&TxInput{
				Address:  []byte("input2"),
				Amount:   111,
				Sequence: 222,
			},
		},
		UnbondTo: []*TxOutput{
			&TxOutput{
				Address: []byte("output1"),
				Amount:  333,
			},
			&TxOutput{
				Address: []byte("output2"),
				Amount:  444,
			},
		},
	}
	signBytes := acm.SignBytes(chainID, bondTx)
	signStr := string(signBytes)
	expected := Fmt(`{"chain_id":"%s","tx":[17,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"pub_key":[1,"3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"],"unbond_to":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
		config.GetString("chain_id"))
	if signStr != expected {
		t.Errorf("Unexpected sign string for BondTx. \nGot %s\nExpected %s", signStr, expected)
	}
}
Пример #24
0
func TxId(tx Tx) []byte {
	signBytes := account.SignBytes(tx)
	return binary.BinaryRipemd160(signBytes)
}
Пример #25
0
func (privVal *PrivValidator) SignVoteUnsafe(vote *types.Vote) {
	vote.Signature = privVal.PrivKey.Sign(account.SignBytes(vote)).(account.SignatureEd25519)
}
Пример #26
0
// If the tx is invalid, an error will be returned.
// Unlike ExecBlock(), state will not be altered.
func ExecTx(blockCache *BlockCache, tx types.Tx, runCall bool, evc events.Fireable) (err error) {

	// TODO: do something with fees
	fees := int64(0)
	_s := blockCache.State() // hack to access validators and block height

	// Exec tx
	switch tx := tx.(type) {
	case *types.SendTx:
		accounts, err := getInputs(blockCache, tx.Inputs)
		if err != nil {
			return err
		}

		// ensure all inputs have send permissions
		if !hasSendPermission(blockCache, accounts) {
			return fmt.Errorf("At least one input lacks permission for SendTx")
		}

		// add outputs to accounts map
		// if any outputs don't exist, all inputs must have CreateAccount perm
		accounts, err = getOrMakeOutputs(blockCache, accounts, tx.Outputs)
		if err != nil {
			return err
		}

		signBytes := acm.SignBytes(_s.ChainID, tx)
		inTotal, err := validateInputs(accounts, signBytes, tx.Inputs)
		if err != nil {
			return err
		}
		outTotal, err := validateOutputs(tx.Outputs)
		if err != nil {
			return err
		}
		if outTotal > inTotal {
			return types.ErrTxInsufficientFunds
		}
		fee := inTotal - outTotal
		fees += fee

		// Good! Adjust accounts
		adjustByInputs(accounts, tx.Inputs)
		adjustByOutputs(accounts, tx.Outputs)
		for _, acc := range accounts {
			blockCache.UpdateAccount(acc)
		}

		// if the evc is nil, nothing will happen
		if evc != nil {
			for _, i := range tx.Inputs {
				evc.FireEvent(types.EventStringAccInput(i.Address), types.EventDataTx{tx, nil, ""})
			}

			for _, o := range tx.Outputs {
				evc.FireEvent(types.EventStringAccOutput(o.Address), types.EventDataTx{tx, nil, ""})
			}
		}
		return nil

	case *types.CallTx:
		var inAcc, outAcc *acm.Account

		// Validate input
		inAcc = blockCache.GetAccount(tx.Input.Address)
		if inAcc == nil {
			log.Info(Fmt("Can't find in account %X", tx.Input.Address))
			return types.ErrTxInvalidAddress
		}

		createContract := len(tx.Address) == 0
		if createContract {
			if !hasCreateContractPermission(blockCache, inAcc) {
				return fmt.Errorf("Account %X does not have CreateContract permission", tx.Input.Address)
			}
		} else {
			if !hasCallPermission(blockCache, inAcc) {
				return fmt.Errorf("Account %X does not have Call permission", tx.Input.Address)
			}
		}

		// pubKey should be present in either "inAcc" or "tx.Input"
		if err := checkInputPubKey(inAcc, tx.Input); err != nil {
			log.Info(Fmt("Can't find pubkey for %X", tx.Input.Address))
			return err
		}
		signBytes := acm.SignBytes(_s.ChainID, tx)
		err := validateInput(inAcc, signBytes, tx.Input)
		if err != nil {
			log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, err))
			return err
		}
		if tx.Input.Amount < tx.Fee {
			log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
			return types.ErrTxInsufficientFunds
		}

		if !createContract {
			// Validate output
			if len(tx.Address) != 20 {
				log.Info(Fmt("Destination address is not 20 bytes %X", tx.Address))
				return types.ErrTxInvalidAddress
			}
			// check if its a native contract
			if vm.RegisteredNativeContract(LeftPadWord256(tx.Address)) {
				return fmt.Errorf("NativeContracts can not be called using CallTx. Use a contract or the appropriate tx type (eg. PermissionsTx, NameTx)")
			}

			// Output account may be nil if we are still in mempool and contract was created in same block as this tx
			// but that's fine, because the account will be created properly when the create tx runs in the block
			// and then this won't return nil. otherwise, we take their fee
			outAcc = blockCache.GetAccount(tx.Address)
		}

		log.Info(Fmt("Out account: %v", outAcc))

		// Good!
		value := tx.Input.Amount - tx.Fee
		inAcc.Sequence += 1
		inAcc.Balance -= tx.Fee
		blockCache.UpdateAccount(inAcc)

		// The logic in runCall MUST NOT return.
		if runCall {

			// VM call variables
			var (
				gas     int64       = tx.GasLimit
				err     error       = nil
				caller  *vm.Account = toVMAccount(inAcc)
				callee  *vm.Account = nil // initialized below
				code    []byte      = nil
				ret     []byte      = nil
				txCache             = NewTxCache(blockCache)
				params              = vm.Params{
					BlockHeight: int64(_s.LastBlockHeight),
					BlockHash:   LeftPadWord256(_s.LastBlockHash),
					BlockTime:   _s.LastBlockTime.Unix(),
					GasLimit:    _s.GetGasLimit(),
				}
			)

			if !createContract && (outAcc == nil || len(outAcc.Code) == 0) {
				// if you call an account that doesn't exist
				// or an account with no code then we take fees (sorry pal)
				// NOTE: it's fine to create a contract and call it within one
				// block (nonce will prevent re-ordering of those txs)
				// but to create with one contract and call with another
				// you have to wait a block to avoid a re-ordering attack
				// that will take your fees
				if outAcc == nil {
					log.Info(Fmt("%X tries to call %X but it does not exist.",
						inAcc.Address, tx.Address))
				} else {
					log.Info(Fmt("%X tries to call %X but code is blank.",
						inAcc.Address, tx.Address))
				}
				err = types.ErrTxInvalidAddress
				goto CALL_COMPLETE
			}

			// get or create callee
			if createContract {
				// We already checked for permission
				callee = txCache.CreateAccount(caller)
				log.Info(Fmt("Created new contract %X", callee.Address))
				code = tx.Data
			} else {
				callee = toVMAccount(outAcc)
				log.Info(Fmt("Calling contract %X with code %X", callee.Address, callee.Code))
				code = callee.Code
			}
			log.Info(Fmt("Code for this contract: %X", code))

			// Run VM call and sync txCache to blockCache.
			{ // Capture scope for goto.
				// Write caller/callee to txCache.
				txCache.UpdateAccount(caller)
				txCache.UpdateAccount(callee)
				vmach := vm.NewVM(txCache, params, caller.Address, types.TxID(_s.ChainID, tx))
				vmach.SetFireable(evc)
				// NOTE: Call() transfers the value from caller to callee iff call succeeds.
				ret, err = vmach.Call(caller, callee, code, tx.Data, value, &gas)
				if err != nil {
					// Failure. Charge the gas fee. The 'value' was otherwise not transferred.
					log.Info(Fmt("Error on execution: %v", err))
					goto CALL_COMPLETE
				}

				log.Info("Successful execution")
				if createContract {
					callee.Code = ret
				}
				txCache.Sync()
			}

		CALL_COMPLETE: // err may or may not be nil.

			// Create a receipt from the ret and whether errored.
			log.Notice("VM call complete", "caller", caller, "callee", callee, "return", ret, "err", err)

			// Fire Events for sender and receiver
			// a separate event will be fired from vm for each additional call
			if evc != nil {
				exception := ""
				if err != nil {
					exception = err.Error()
				}
				evc.FireEvent(types.EventStringAccInput(tx.Input.Address), types.EventDataTx{tx, ret, exception})
				evc.FireEvent(types.EventStringAccOutput(tx.Address), types.EventDataTx{tx, ret, exception})
			}
		} else {
			// The mempool does not call txs until
			// the proposer determines the order of txs.
			// So mempool will skip the actual .Call(),
			// and only deduct from the caller's balance.
			inAcc.Balance -= value
			if createContract {
				inAcc.Sequence += 1
			}
			blockCache.UpdateAccount(inAcc)
		}

		return nil

	case *types.NameTx:
		var inAcc *acm.Account

		// Validate input
		inAcc = blockCache.GetAccount(tx.Input.Address)
		if inAcc == nil {
			log.Info(Fmt("Can't find in account %X", tx.Input.Address))
			return types.ErrTxInvalidAddress
		}
		// check permission
		if !hasNamePermission(blockCache, inAcc) {
			return fmt.Errorf("Account %X does not have Name permission", tx.Input.Address)
		}
		// pubKey should be present in either "inAcc" or "tx.Input"
		if err := checkInputPubKey(inAcc, tx.Input); err != nil {
			log.Info(Fmt("Can't find pubkey for %X", tx.Input.Address))
			return err
		}
		signBytes := acm.SignBytes(_s.ChainID, tx)
		err := validateInput(inAcc, signBytes, tx.Input)
		if err != nil {
			log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, err))
			return err
		}
		// fee is in addition to the amount which is used to determine the TTL
		if tx.Input.Amount < tx.Fee {
			log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
			return types.ErrTxInsufficientFunds
		}

		// validate the input strings
		if err := tx.ValidateStrings(); err != nil {
			return err
		}

		value := tx.Input.Amount - tx.Fee

		// let's say cost of a name for one block is len(data) + 32
		costPerBlock := types.NameCostPerBlock(types.NameBaseCost(tx.Name, tx.Data))
		expiresIn := int(value / costPerBlock)
		lastBlockHeight := _s.LastBlockHeight

		log.Info("New NameTx", "value", value, "costPerBlock", costPerBlock, "expiresIn", expiresIn, "lastBlock", lastBlockHeight)

		// check if the name exists
		entry := blockCache.GetNameRegEntry(tx.Name)

		if entry != nil {
			var expired bool
			// if the entry already exists, and hasn't expired, we must be owner
			if entry.Expires > lastBlockHeight {
				// ensure we are owner
				if bytes.Compare(entry.Owner, tx.Input.Address) != 0 {
					log.Info(Fmt("Sender %X is trying to update a name (%s) for which he is not owner", tx.Input.Address, tx.Name))
					return types.ErrTxPermissionDenied
				}
			} else {
				expired = true
			}

			// no value and empty data means delete the entry
			if value == 0 && len(tx.Data) == 0 {
				// maybe we reward you for telling us we can delete this crap
				// (owners if not expired, anyone if expired)
				log.Info("Removing namereg entry", "name", entry.Name)
				blockCache.RemoveNameRegEntry(entry.Name)
			} else {
				// update the entry by bumping the expiry
				// and changing the data
				if expired {
					if expiresIn < types.MinNameRegistrationPeriod {
						return errors.New(Fmt("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod))
					}
					entry.Expires = lastBlockHeight + expiresIn
					entry.Owner = tx.Input.Address
					log.Info("An old namereg entry has expired and been reclaimed", "name", entry.Name, "expiresIn", expiresIn, "owner", entry.Owner)
				} else {
					// since the size of the data may have changed
					// we use the total amount of "credit"
					oldCredit := int64(entry.Expires-lastBlockHeight) * types.NameBaseCost(entry.Name, entry.Data)
					credit := oldCredit + value
					expiresIn = int(credit / costPerBlock)
					if expiresIn < types.MinNameRegistrationPeriod {
						return errors.New(Fmt("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod))
					}
					entry.Expires = lastBlockHeight + expiresIn
					log.Info("Updated namereg entry", "name", entry.Name, "expiresIn", expiresIn, "oldCredit", oldCredit, "value", value, "credit", credit)
				}
				entry.Data = tx.Data
				blockCache.UpdateNameRegEntry(entry)
			}
		} else {
			if expiresIn < types.MinNameRegistrationPeriod {
				return errors.New(Fmt("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod))
			}
			// entry does not exist, so create it
			entry = &types.NameRegEntry{
				Name:    tx.Name,
				Owner:   tx.Input.Address,
				Data:    tx.Data,
				Expires: lastBlockHeight + expiresIn,
			}
			log.Info("Creating namereg entry", "name", entry.Name, "expiresIn", expiresIn)
			blockCache.UpdateNameRegEntry(entry)
		}

		// TODO: something with the value sent?

		// Good!
		inAcc.Sequence += 1
		inAcc.Balance -= value
		blockCache.UpdateAccount(inAcc)

		// TODO: maybe we want to take funds on error and allow txs in that don't do anythingi?

		if evc != nil {
			evc.FireEvent(types.EventStringAccInput(tx.Input.Address), types.EventDataTx{tx, nil, ""})
			evc.FireEvent(types.EventStringNameReg(tx.Name), types.EventDataTx{tx, nil, ""})
		}

		return nil

	case *types.BondTx:
		valInfo := blockCache.State().GetValidatorInfo(tx.PubKey.Address())
		if valInfo != nil {
			// TODO: In the future, check that the validator wasn't destroyed,
			// add funds, merge UnbondTo outputs, and unbond validator.
			return errors.New("Adding coins to existing validators not yet supported")
		}

		accounts, err := getInputs(blockCache, tx.Inputs)
		if err != nil {
			return err
		}

		// add outputs to accounts map
		// if any outputs don't exist, all inputs must have CreateAccount perm
		// though outputs aren't created until unbonding/release time
		canCreate := hasCreateAccountPermission(blockCache, accounts)
		for _, out := range tx.UnbondTo {
			acc := blockCache.GetAccount(out.Address)
			if acc == nil && !canCreate {
				return fmt.Errorf("At least one input does not have permission to create accounts")
			}
		}

		bondAcc := blockCache.GetAccount(tx.PubKey.Address())
		if !hasBondPermission(blockCache, bondAcc) {
			return fmt.Errorf("The bonder does not have permission to bond")
		}

		if !hasBondOrSendPermission(blockCache, accounts) {
			return fmt.Errorf("At least one input lacks permission to bond")
		}

		signBytes := acm.SignBytes(_s.ChainID, tx)
		inTotal, err := validateInputs(accounts, signBytes, tx.Inputs)
		if err != nil {
			return err
		}
		if !tx.PubKey.VerifyBytes(signBytes, tx.Signature) {
			return types.ErrTxInvalidSignature
		}
		outTotal, err := validateOutputs(tx.UnbondTo)
		if err != nil {
			return err
		}
		if outTotal > inTotal {
			return types.ErrTxInsufficientFunds
		}
		fee := inTotal - outTotal
		fees += fee

		// Good! Adjust accounts
		adjustByInputs(accounts, tx.Inputs)
		for _, acc := range accounts {
			blockCache.UpdateAccount(acc)
		}
		// Add ValidatorInfo
		_s.SetValidatorInfo(&types.ValidatorInfo{
			Address:         tx.PubKey.Address(),
			PubKey:          tx.PubKey,
			UnbondTo:        tx.UnbondTo,
			FirstBondHeight: _s.LastBlockHeight + 1,
			FirstBondAmount: outTotal,
		})
		// Add Validator
		added := _s.BondedValidators.Add(&types.Validator{
			Address:     tx.PubKey.Address(),
			PubKey:      tx.PubKey,
			BondHeight:  _s.LastBlockHeight + 1,
			VotingPower: outTotal,
			Accum:       0,
		})
		if !added {
			PanicCrisis("Failed to add validator")
		}
		if evc != nil {
			// TODO: fire for all inputs
			evc.FireEvent(types.EventStringBond(), types.EventDataTx{tx, nil, ""})
		}
		return nil

	case *types.UnbondTx:
		// The validator must be active
		_, val := _s.BondedValidators.GetByAddress(tx.Address)
		if val == nil {
			return types.ErrTxInvalidAddress
		}

		// Verify the signature
		signBytes := acm.SignBytes(_s.ChainID, tx)
		if !val.PubKey.VerifyBytes(signBytes, tx.Signature) {
			return types.ErrTxInvalidSignature
		}

		// tx.Height must be greater than val.LastCommitHeight
		if tx.Height <= val.LastCommitHeight {
			return errors.New("Invalid unbond height")
		}

		// Good!
		_s.unbondValidator(val)
		if evc != nil {
			evc.FireEvent(types.EventStringUnbond(), types.EventDataTx{tx, nil, ""})
		}
		return nil

	case *types.RebondTx:
		// The validator must be inactive
		_, val := _s.UnbondingValidators.GetByAddress(tx.Address)
		if val == nil {
			return types.ErrTxInvalidAddress
		}

		// Verify the signature
		signBytes := acm.SignBytes(_s.ChainID, tx)
		if !val.PubKey.VerifyBytes(signBytes, tx.Signature) {
			return types.ErrTxInvalidSignature
		}

		// tx.Height must be in a suitable range
		minRebondHeight := _s.LastBlockHeight - (validatorTimeoutBlocks / 2)
		maxRebondHeight := _s.LastBlockHeight + 2
		if !((minRebondHeight <= tx.Height) && (tx.Height <= maxRebondHeight)) {
			return errors.New(Fmt("Rebond height not in range.  Expected %v <= %v <= %v",
				minRebondHeight, tx.Height, maxRebondHeight))
		}

		// Good!
		_s.rebondValidator(val)
		if evc != nil {
			evc.FireEvent(types.EventStringRebond(), types.EventDataTx{tx, nil, ""})
		}
		return nil

	case *types.DupeoutTx:
		// Verify the signatures
		_, accused := _s.BondedValidators.GetByAddress(tx.Address)
		if accused == nil {
			_, accused = _s.UnbondingValidators.GetByAddress(tx.Address)
			if accused == nil {
				return types.ErrTxInvalidAddress
			}
		}
		voteASignBytes := acm.SignBytes(_s.ChainID, &tx.VoteA)
		voteBSignBytes := acm.SignBytes(_s.ChainID, &tx.VoteB)
		if !accused.PubKey.VerifyBytes(voteASignBytes, tx.VoteA.Signature) ||
			!accused.PubKey.VerifyBytes(voteBSignBytes, tx.VoteB.Signature) {
			return types.ErrTxInvalidSignature
		}

		// Verify equivocation
		// TODO: in the future, just require one vote from a previous height that
		// doesn't exist on this chain.
		if tx.VoteA.Height != tx.VoteB.Height {
			return errors.New("DupeoutTx heights don't match")
		}
		if tx.VoteA.Round != tx.VoteB.Round {
			return errors.New("DupeoutTx rounds don't match")
		}
		if tx.VoteA.Type != tx.VoteB.Type {
			return errors.New("DupeoutTx types don't match")
		}
		if bytes.Equal(tx.VoteA.BlockHash, tx.VoteB.BlockHash) {
			return errors.New("DupeoutTx blockhashes shouldn't match")
		}

		// Good! (Bad validator!)
		_s.destroyValidator(accused)
		if evc != nil {
			evc.FireEvent(types.EventStringDupeout(), types.EventDataTx{tx, nil, ""})
		}
		return nil

	case *types.PermissionsTx:
		var inAcc *acm.Account

		// Validate input
		inAcc = blockCache.GetAccount(tx.Input.Address)
		if inAcc == nil {
			log.Debug(Fmt("Can't find in account %X", tx.Input.Address))
			return types.ErrTxInvalidAddress
		}

		permFlag := tx.PermArgs.PermFlag()
		// check permission
		if !HasPermission(blockCache, inAcc, permFlag) {
			return fmt.Errorf("Account %X does not have moderator permission %s (%b)", tx.Input.Address, ptypes.PermFlagToString(permFlag), permFlag)
		}

		// pubKey should be present in either "inAcc" or "tx.Input"
		if err := checkInputPubKey(inAcc, tx.Input); err != nil {
			log.Debug(Fmt("Can't find pubkey for %X", tx.Input.Address))
			return err
		}
		signBytes := acm.SignBytes(_s.ChainID, tx)
		err := validateInput(inAcc, signBytes, tx.Input)
		if err != nil {
			log.Debug(Fmt("validateInput failed on %X: %v", tx.Input.Address, err))
			return err
		}

		value := tx.Input.Amount

		log.Debug("New PermissionsTx", "function", ptypes.PermFlagToString(permFlag), "args", tx.PermArgs)

		var permAcc *acm.Account
		switch args := tx.PermArgs.(type) {
		case *ptypes.HasBaseArgs:
			// this one doesn't make sense from txs
			return fmt.Errorf("HasBase is for contracts, not humans. Just look at the blockchain")
		case *ptypes.SetBaseArgs:
			if permAcc = blockCache.GetAccount(args.Address); permAcc == nil {
				return fmt.Errorf("Trying to update permissions for unknown account %X", args.Address)
			}
			err = permAcc.Permissions.Base.Set(args.Permission, args.Value)
		case *ptypes.UnsetBaseArgs:
			if permAcc = blockCache.GetAccount(args.Address); permAcc == nil {
				return fmt.Errorf("Trying to update permissions for unknown account %X", args.Address)
			}
			err = permAcc.Permissions.Base.Unset(args.Permission)
		case *ptypes.SetGlobalArgs:
			if permAcc = blockCache.GetAccount(ptypes.GlobalPermissionsAddress); permAcc == nil {
				PanicSanity("can't find global permissions account")
			}
			err = permAcc.Permissions.Base.Set(args.Permission, args.Value)
		case *ptypes.HasRoleArgs:
			return fmt.Errorf("HasRole is for contracts, not humans. Just look at the blockchain")
		case *ptypes.AddRoleArgs:
			if permAcc = blockCache.GetAccount(args.Address); permAcc == nil {
				return fmt.Errorf("Trying to update roles for unknown account %X", args.Address)
			}
			if !permAcc.Permissions.AddRole(args.Role) {
				return fmt.Errorf("Role (%s) already exists for account %X", args.Role, args.Address)
			}
		case *ptypes.RmRoleArgs:
			if permAcc = blockCache.GetAccount(args.Address); permAcc == nil {
				return fmt.Errorf("Trying to update roles for unknown account %X", args.Address)
			}
			if !permAcc.Permissions.RmRole(args.Role) {
				return fmt.Errorf("Role (%s) does not exist for account %X", args.Role, args.Address)
			}
		default:
			PanicSanity(Fmt("invalid permission function: %s", ptypes.PermFlagToString(permFlag)))
		}

		// TODO: maybe we want to take funds on error and allow txs in that don't do anythingi?
		if err != nil {
			return err
		}

		// Good!
		inAcc.Sequence += 1
		inAcc.Balance -= value
		blockCache.UpdateAccount(inAcc)
		if permAcc != nil {
			blockCache.UpdateAccount(permAcc)
		}

		if evc != nil {
			evc.FireEvent(types.EventStringAccInput(tx.Input.Address), types.EventDataTx{tx, nil, ""})
			evc.FireEvent(types.EventStringPermissions(ptypes.PermFlagToString(permFlag)), types.EventDataTx{tx, nil, ""})
		}

		return nil

	default:
		// binary decoding should not let this happen
		PanicSanity("Unknown Tx type")
		return nil
	}
}
Пример #27
0
func (privVal *PrivValidator) SignVoteUnsafe(chainID string, vote *Vote) {
	vote.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, vote)).(acm.SignatureEd25519)
}
Пример #28
0
// This should match the leaf hashes of Block.Data.Hash()'s SimpleMerkleTree.
func TxID(chainID string, tx Tx) []byte {
	signBytes := acm.SignBytes(chainID, tx)
	return wire.BinaryRipemd160(signBytes)
}
Пример #29
0
func gen_tx() {

	// Get State, which may be nil.
	stateDB := dbm.GetDB("state")
	state := sm.LoadState(stateDB)

	// Get source pubkey
	srcPubKeyBytes := getByteSliceFromHex("Enter source pubkey: ")
	r, n, err := bytes.NewReader(srcPubKeyBytes), new(int64), new(error)
	srcPubKey := binary.ReadBinary(struct{ account.PubKey }{}, r, n, err).(struct{ account.PubKey }).PubKey
	if *err != nil {
		Exit(Fmt("Invalid PubKey. Error: %v", err))
	}

	// Get the state of the account.
	var srcAccount *account.Account
	var srcAccountAddress = srcPubKey.Address()
	var srcAccountBalanceStr = "unknown"
	var srcAccountSequenceStr = "unknown"
	srcAddress := srcPubKey.Address()
	if state != nil {
		srcAccount = state.GetAccount(srcAddress)
		srcAccountBalanceStr = Fmt("%v", srcAccount.Balance)
		srcAccountSequenceStr = Fmt("%v", srcAccount.Sequence+1)
	}

	// Get the amount to send from src account
	srcSendAmount := getUint64(Fmt("Enter amount to send from %X (total: %v): ", srcAccountAddress, srcAccountBalanceStr))

	// Get the next sequence of src account
	srcSendSequence := uint(getUint64(Fmt("Enter next sequence for %X (guess: %v): ", srcAccountAddress, srcAccountSequenceStr)))

	// Get dest address
	dstAddress := getByteSliceFromHex("Enter destination address: ")

	// Get the amount to send to dst account
	dstSendAmount := getUint64(Fmt("Enter amount to send to %X: ", dstAddress))

	// Construct SendTx
	tx := &types.SendTx{
		Inputs: []*types.TxInput{
			&types.TxInput{
				Address:   srcAddress,
				Amount:    srcSendAmount,
				Sequence:  srcSendSequence,
				Signature: account.SignatureEd25519{},
				PubKey:    srcPubKey,
			},
		},
		Outputs: []*types.TxOutput{
			&types.TxOutput{
				Address: dstAddress,
				Amount:  dstSendAmount,
			},
		},
	}

	// Show the intermediate form.
	fmt.Printf("Generated tx: %X\n", binary.BinaryBytes(tx))

	// Get source privkey (for signing)
	srcPrivKeyBytes := getByteSliceFromHex("Enter source privkey (for signing): ")
	r, n, err = bytes.NewReader(srcPrivKeyBytes), new(int64), new(error)
	srcPrivKey := binary.ReadBinary(struct{ account.PrivKey }{}, r, n, err).(struct{ account.PrivKey }).PrivKey
	if *err != nil {
		Exit(Fmt("Invalid PrivKey. Error: %v", err))
	}

	// Sign
	tx.Inputs[0].Signature = srcPrivKey.Sign(account.SignBytes(tx))
	fmt.Printf("Signed tx: %X\n", binary.BinaryBytes(tx))
}
Пример #30
0
// executes transactions of a block, does not check block.StateHash
// NOTE: If an error occurs during block execution, state will be left
// at an invalid state.  Copy the state before calling execBlock!
func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader) error {
	// Basic block validation.
	err := block.ValidateBasic(s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime)
	if err != nil {
		return err
	}

	// Validate block Validation.
	if block.Height == 1 {
		if len(block.Validation.Commits) != 0 {
			return errors.New("Block at height 1 (first block) should have no Validation commits")
		}
	} else {
		if uint(len(block.Validation.Commits)) != s.LastBondedValidators.Size() {
			return errors.New(Fmt("Invalid block validation size. Expected %v, got %v",
				s.LastBondedValidators.Size(), len(block.Validation.Commits)))
		}
		var sumVotingPower uint64
		s.LastBondedValidators.Iterate(func(index uint, val *Validator) bool {
			commit := block.Validation.Commits[index]
			if commit.IsZero() {
				return false
			} else {
				vote := &types.Vote{
					Height:     block.Height - 1,
					Round:      commit.Round,
					Type:       types.VoteTypeCommit,
					BlockHash:  block.LastBlockHash,
					BlockParts: block.LastBlockParts,
				}
				if val.PubKey.VerifyBytes(account.SignBytes(vote), commit.Signature) {
					sumVotingPower += val.VotingPower
					return false
				} else {
					log.Warn(Fmt("Invalid validation signature.\nval: %v\nvote: %v", val, vote))
					err = errors.New("Invalid validation signature")
					return true
				}
			}
		})
		if err != nil {
			return err
		}
		if sumVotingPower <= s.LastBondedValidators.TotalVotingPower()*2/3 {
			return errors.New("Insufficient validation voting power")
		}
	}

	// Update Validator.LastCommitHeight as necessary.
	for i, commit := range block.Validation.Commits {
		if commit.IsZero() {
			continue
		}
		_, val := s.LastBondedValidators.GetByIndex(uint(i))
		if val == nil {
			panic(Fmt("Failed to fetch validator at index %v", i))
		}
		if _, val_ := s.BondedValidators.GetByAddress(val.Address); val_ != nil {
			val_.LastCommitHeight = block.Height - 1
			updated := s.BondedValidators.Update(val_)
			if !updated {
				panic("Failed to update bonded validator LastCommitHeight")
			}
		} else if _, val_ := s.UnbondingValidators.GetByAddress(val.Address); val_ != nil {
			val_.LastCommitHeight = block.Height - 1
			updated := s.UnbondingValidators.Update(val_)
			if !updated {
				panic("Failed to update unbonding validator LastCommitHeight")
			}
		} else {
			panic("Could not find validator")
		}
	}

	// Remember LastBondedValidators
	s.LastBondedValidators = s.BondedValidators.Copy()

	// Create BlockCache to cache changes to state.
	blockCache := NewBlockCache(s)

	// Commit each tx
	for _, tx := range block.Data.Txs {
		err := ExecTx(blockCache, tx, true, s.evc)
		if err != nil {
			return InvalidTxError{tx, err}
		}
	}

	// Now sync the BlockCache to the backend.
	blockCache.Sync()

	// If any unbonding periods are over,
	// reward account with bonded coins.
	toRelease := []*Validator{}
	s.UnbondingValidators.Iterate(func(index uint, val *Validator) bool {
		if val.UnbondHeight+unbondingPeriodBlocks < block.Height {
			toRelease = append(toRelease, val)
		}
		return false
	})
	for _, val := range toRelease {
		s.releaseValidator(val)
	}

	// If any validators haven't signed in a while,
	// unbond them, they have timed out.
	toTimeout := []*Validator{}
	s.BondedValidators.Iterate(func(index uint, val *Validator) bool {
		lastActivityHeight := MaxUint(val.BondHeight, val.LastCommitHeight)
		if lastActivityHeight+validatorTimeoutBlocks < block.Height {
			log.Info("Validator timeout", "validator", val, "height", block.Height)
			toTimeout = append(toTimeout, val)
		}
		return false
	})
	for _, val := range toTimeout {
		s.unbondValidator(val)
	}

	// Increment validator AccumPowers
	s.BondedValidators.IncrementAccum(1)
	s.LastBlockHeight = block.Height
	s.LastBlockHash = block.Hash()
	s.LastBlockParts = blockPartsHeader
	s.LastBlockTime = block.Time
	return nil
}