Browse Source

Bare consensus refactor

pull/164/head
Jae Kwon 9 years ago
parent
commit
e12f9d10e7
101 changed files with 342 additions and 12709 deletions
  1. +11
    -11
      Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection.go
  2. +6
    -6
      Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection_test.go
  3. +4
    -4
      Godeps/_workspace/src/github.com/tendermint/go-p2p/switch.go
  4. +5
    -5
      Godeps/_workspace/src/github.com/tendermint/go-p2p/switch_test.go
  5. +0
    -3
      Makefile
  6. +0
    -74
      account/account.go
  7. +0
    -85
      account/priv_account.go
  8. +0
    -70
      account/priv_key.go
  9. +0
    -91
      account/pub_key.go
  10. +0
    -34
      account/signature.go
  11. +0
    -71
      account/signature_test.go
  12. +3
    -3
      blockchain/reactor.go
  13. +2
    -2
      cmd/barak/types/command.go
  14. +2
    -2
      cmd/barak/types/validator.go
  15. +2
    -2
      cmd/barak/validate.go
  16. +17
    -17
      cmd/debora/commands.go
  17. +2
    -2
      cmd/debora/main.go
  18. +0
    -10
      cmd/sim_txs/README.md
  19. +0
    -169
      cmd/sim_txs/main.go
  20. +0
    -43
      cmd/tendermint/gen_account.go
  21. +0
    -42
      cmd/tendermint/get_account.go
  22. +0
    -9
      cmd/tendermint/main.go
  23. +0
    -120
      cmd/tendermint/send_tx.go
  24. +0
    -1
      config/tendermint/config.go
  25. +0
    -2
      config/tendermint_test/config.go
  26. +10
    -186
      consensus/state.go
  27. +2
    -32
      consensus/state_test.go
  28. +5
    -6
      consensus/test.go
  29. +0
    -312
      crawler/crawl.go
  30. +0
    -7
      crawler/log.go
  31. BIN
      docs/sts-final.pdf
  32. +6
    -14
      mempool/mempool.go
  33. +0
    -273
      mempool/mempool_test.go
  34. +4
    -4
      node/id.go
  35. +26
    -32
      node/node.go
  36. +2
    -2
      node/node_test.go
  37. +0
    -23
      permission/types/errors.go
  38. +0
    -238
      permission/types/permissions.go
  39. +0
    -102
      permission/types/snatives.go
  40. +1
    -1
      rpc/client/client.go
  41. +0
    -67
      rpc/core/accounts.go
  42. +5
    -10
      rpc/core/consensus.go
  43. +1
    -13
      rpc/core/mempool.go
  44. +0
    -29
      rpc/core/names.go
  45. +3
    -4
      rpc/core/pipe.go
  46. +9
    -19
      rpc/core/routes.go
  47. +0
    -116
      rpc/core/txs.go
  48. +14
    -87
      rpc/core/types/responses.go
  49. +0
    -236
      rpc/core_client/client.go
  50. +0
    -1042
      rpc/core_client/client_methods.go
  51. +0
    -8
      rpc/core_client/log.go
  52. +0
    -119
      rpc/core_client/ws_client.go
  53. +0
    -129
      rpc/test/client_rpc_test.go
  54. +0
    -212
      rpc/test/client_ws_test.go
  55. +0
    -13
      rpc/test/config.go
  56. +0
    -282
      rpc/test/helpers.go
  57. +0
    -281
      rpc/test/tests.go
  58. +0
    -271
      rpc/test/ws_helpers.go
  59. +0
    -293
      state/block_cache.go
  60. +0
    -18
      state/common.go
  61. +16
    -892
      state/execution.go
  62. +0
    -87
      state/genesis_test.go
  63. +0
    -1265
      state/permissions_test.go
  64. +41
    -350
      state/state.go
  65. +0
    -699
      state/state_test.go
  66. +0
    -198
      state/tx_cache.go
  67. +0
    -23
      state/tx_cache_test.go
  68. +0
    -121
      state/types/genesis.go
  69. +3
    -4
      types/block.go
  70. +65
    -0
      types/genesis.go
  71. +0
    -55
      types/names.go
  72. +0
    -67
      types/node.go
  73. +11
    -33
      types/priv_validator.go
  74. +6
    -6
      types/proposal.go
  75. +1
    -2
      types/proposal_test.go
  76. +30
    -0
      types/signable.go
  77. +1
    -373
      types/tx.go
  78. +0
    -178
      types/tx_test.go
  79. +0
    -260
      types/tx_utils.go
  80. +6
    -42
      types/validator.go
  81. +1
    -2
      types/validator_set.go
  82. +7
    -10
      types/validator_set_test.go
  83. +2
    -2
      types/vote.go
  84. +9
    -22
      types/vote_set.go
  85. +1
    -1
      types/vote_set_test.go
  86. +0
    -0
      vm/.ethtest
  87. +0
    -26
      vm/common.go
  88. +0
    -18
      vm/gas.go
  89. +0
    -7
      vm/log.go
  90. +0
    -105
      vm/native.go
  91. +0
    -354
      vm/opcodes.go
  92. +0
    -35
      vm/randentropy/rand_entropy.go
  93. +0
    -171
      vm/sha3/keccakf.go
  94. +0
    -224
      vm/sha3/sha3.go
  95. +0
    -233
      vm/snative.go
  96. +0
    -126
      vm/stack.go
  97. +0
    -79
      vm/test/fake_app_state.go
  98. +0
    -90
      vm/test/log_event_test.go
  99. +0
    -207
      vm/test/vm_test.go
  100. +0
    -49
      vm/types.go

+ 11
- 11
Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection.go View File

@ -20,7 +20,7 @@ import (
"golang.org/x/crypto/nacl/secretbox"
"golang.org/x/crypto/ripemd160"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
)
@ -38,7 +38,7 @@ type SecretConnection struct {
recvBuffer []byte
recvNonce *[24]byte
sendNonce *[24]byte
remPubKey acm.PubKeyEd25519
remPubKey crypto.PubKeyEd25519
shrSecret *[32]byte // shared secret
}
@ -46,9 +46,9 @@ type SecretConnection struct {
// Returns nil if error in handshake.
// Caller should call conn.Close()
// See docs/sts-final.pdf for more information.
func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey acm.PrivKeyEd25519) (*SecretConnection, error) {
func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKeyEd25519) (*SecretConnection, error) {
locPubKey := locPrivKey.PubKey().(acm.PubKeyEd25519)
locPubKey := locPrivKey.PubKey().(crypto.PubKeyEd25519)
// Generate ephemeral keys for perfect forward secrecy.
locEphPub, locEphPriv := genEphKeys()
@ -101,7 +101,7 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey acm.PrivKeyEd25519
}
// Returns authenticated remote pubkey
func (sc *SecretConnection) RemotePubKey() acm.PubKeyEd25519 {
func (sc *SecretConnection) RemotePubKey() crypto.PubKeyEd25519 {
return sc.remPubKey
}
@ -254,17 +254,17 @@ func genChallenge(loPubKey, hiPubKey *[32]byte) (challenge *[32]byte) {
return hash32(append(loPubKey[:], hiPubKey[:]...))
}
func signChallenge(challenge *[32]byte, locPrivKey acm.PrivKeyEd25519) (signature acm.SignatureEd25519) {
signature = locPrivKey.Sign(challenge[:]).(acm.SignatureEd25519)
func signChallenge(challenge *[32]byte, locPrivKey crypto.PrivKeyEd25519) (signature crypto.SignatureEd25519) {
signature = locPrivKey.Sign(challenge[:]).(crypto.SignatureEd25519)
return
}
type authSigMessage struct {
Key acm.PubKeyEd25519
Sig acm.SignatureEd25519
Key crypto.PubKeyEd25519
Sig crypto.SignatureEd25519
}
func shareAuthSignature(sc *SecretConnection, pubKey acm.PubKeyEd25519, signature acm.SignatureEd25519) (*authSigMessage, error) {
func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKeyEd25519, signature crypto.SignatureEd25519) (*authSigMessage, error) {
var recvMsg authSigMessage
var err1, err2 error
@ -293,7 +293,7 @@ func shareAuthSignature(sc *SecretConnection, pubKey acm.PubKeyEd25519, signatur
return &recvMsg, nil
}
func verifyChallengeSignature(challenge *[32]byte, remPubKey acm.PubKeyEd25519, remSignature acm.SignatureEd25519) bool {
func verifyChallengeSignature(challenge *[32]byte, remPubKey crypto.PubKeyEd25519, remSignature crypto.SignatureEd25519) bool {
return remPubKey.VerifyBytes(challenge[:], remSignature)
}


+ 6
- 6
Godeps/_workspace/src/github.com/tendermint/go-p2p/secret_connection_test.go View File

@ -5,7 +5,7 @@ import (
"io"
"testing"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
. "github.com/tendermint/go-common"
)
@ -32,10 +32,10 @@ func makeDummyConnPair() (fooConn, barConn dummyConn) {
func makeSecretConnPair(tb testing.TB) (fooSecConn, barSecConn *SecretConnection) {
fooConn, barConn := makeDummyConnPair()
fooPrvKey := acm.GenPrivKeyEd25519()
fooPubKey := fooPrvKey.PubKey().(acm.PubKeyEd25519)
barPrvKey := acm.GenPrivKeyEd25519()
barPubKey := barPrvKey.PubKey().(acm.PubKeyEd25519)
fooPrvKey := crypto.GenPrivKeyEd25519()
fooPubKey := fooPrvKey.PubKey().(crypto.PubKeyEd25519)
barPrvKey := crypto.GenPrivKeyEd25519()
barPubKey := barPrvKey.PubKey().(crypto.PubKeyEd25519)
Parallel(
func() {
@ -89,7 +89,7 @@ func TestSecretConnectionReadWrite(t *testing.T) {
genNodeRunner := func(nodeConn dummyConn, nodeWrites []string, nodeReads *[]string) func() {
return func() {
// Node handskae
nodePrvKey := acm.GenPrivKeyEd25519()
nodePrvKey := crypto.GenPrivKeyEd25519()
nodeSecretConn, err := MakeSecretConnection(nodeConn, nodePrvKey)
if err != nil {
t.Errorf("Failed to establish SecretConnection for node: %v", err)


+ 4
- 4
Godeps/_workspace/src/github.com/tendermint/go-p2p/switch.go View File

@ -8,7 +8,7 @@ import (
"time"
"github.com/tendermint/log15"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/types"
)
@ -63,7 +63,7 @@ type Switch struct {
peers *PeerSet
dialing *CMap
nodeInfo *types.NodeInfo // our node info
nodePrivKey acm.PrivKeyEd25519 // our node privkey
nodePrivKey crypto.PrivKeyEd25519 // our node privkey
}
var (
@ -145,10 +145,10 @@ func (sw *Switch) NodeInfo() *types.NodeInfo {
// Not goroutine safe.
// NOTE: Overwrites sw.nodeInfo.PubKey
func (sw *Switch) SetNodePrivKey(nodePrivKey acm.PrivKeyEd25519) {
func (sw *Switch) SetNodePrivKey(nodePrivKey crypto.PrivKeyEd25519) {
sw.nodePrivKey = nodePrivKey
if sw.nodeInfo != nil {
sw.nodeInfo.PubKey = nodePrivKey.PubKey().(acm.PubKeyEd25519)
sw.nodeInfo.PubKey = nodePrivKey.PubKey().(crypto.PubKeyEd25519)
}
}


+ 5
- 5
Godeps/_workspace/src/github.com/tendermint/go-p2p/switch_test.go View File

@ -6,7 +6,7 @@ import (
"testing"
"time"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
. "github.com/tendermint/go-common"
_ "github.com/tendermint/go-config/tendermint_test"
"github.com/tendermint/tendermint/types"
@ -72,13 +72,13 @@ func (tr *TestReactor) Receive(chID byte, peer *Peer, msgBytes []byte) {
// convenience method for creating two switches connected to each other.
func makeSwitchPair(t testing.TB, initSwitch func(*Switch) *Switch) (*Switch, *Switch) {
s1PrivKey := acm.GenPrivKeyEd25519()
s2PrivKey := acm.GenPrivKeyEd25519()
s1PrivKey := crypto.GenPrivKeyEd25519()
s2PrivKey := crypto.GenPrivKeyEd25519()
// Create two switches that will be interconnected.
s1 := initSwitch(NewSwitch())
s1.SetNodeInfo(&types.NodeInfo{
PubKey: s1PrivKey.PubKey().(acm.PubKeyEd25519),
PubKey: s1PrivKey.PubKey().(crypto.PubKeyEd25519),
Moniker: "switch1",
ChainID: "testing",
Version: types.Versions{Tendermint: "123.123.123"},
@ -86,7 +86,7 @@ func makeSwitchPair(t testing.TB, initSwitch func(*Switch) *Switch) (*Switch, *S
s1.SetNodePrivKey(s1PrivKey)
s2 := initSwitch(NewSwitch())
s2.SetNodeInfo(&types.NodeInfo{
PubKey: s2PrivKey.PubKey().(acm.PubKeyEd25519),
PubKey: s2PrivKey.PubKey().(crypto.PubKeyEd25519),
Moniker: "switch2",
ChainID: "testing",
Version: types.Versions{Tendermint: "123.123.123"},


+ 0
- 3
Makefile View File

@ -10,7 +10,6 @@ install:
go install github.com/tendermint/tendermint/cmd/debora
go install github.com/tendermint/tendermint/cmd/stdinwriter
go install github.com/tendermint/tendermint/cmd/logjack
go install github.com/tendermint/tendermint/cmd/sim_txs
build:
go build -o build/tendermint github.com/tendermint/tendermint/cmd/tendermint
@ -18,7 +17,6 @@ build:
go build -o build/debora github.com/tendermint/tendermint/cmd/debora
go build -o build/stdinwriter github.com/tendermint/tendermint/cmd/stdinwriter
go build -o build/logjack github.com/tendermint/tendermint/cmd/logjack
go build -o build/sim_txs github.com/tendermint/tendermint/cmd/sim_txs
build_race:
go build -race -o build/tendermint github.com/tendermint/tendermint/cmd/tendermint
@ -26,7 +24,6 @@ build_race:
go build -race -o build/debora github.com/tendermint/tendermint/cmd/debora
go build -race -o build/stdinwriter github.com/tendermint/tendermint/cmd/stdinwriter
go build -race -o build/logjack github.com/tendermint/tendermint/cmd/logjack
go build -race -o build/sim_txs github.com/tendermint/tendermint/cmd/sim_txs
test: build
-rm -rf ~/.tendermint_test_bak


+ 0
- 74
account/account.go View File

@ -1,74 +0,0 @@
package account
import (
"bytes"
"fmt"
"io"
"github.com/tendermint/go-wire"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-merkle"
ptypes "github.com/tendermint/tendermint/permission/types"
)
// Signable is an interface for all signable things.
// It typically removes signatures before serializing.
type Signable interface {
WriteSignBytes(chainID string, w io.Writer, n *int64, err *error)
}
// SignBytes is a convenience method for getting the bytes to sign of a Signable.
func SignBytes(chainID string, o Signable) []byte {
buf, n, err := new(bytes.Buffer), new(int64), new(error)
o.WriteSignBytes(chainID, buf, n, err)
if *err != nil {
PanicCrisis(err)
}
return buf.Bytes()
}
// HashSignBytes is a convenience method for getting the hash of the bytes of a signable
func HashSignBytes(chainID string, o Signable) []byte {
return merkle.SimpleHashFromBinary(SignBytes(chainID, o))
}
//-----------------------------------------------------------------------------
// Account resides in the application state, and is mutated by transactions
// on the blockchain.
// Serialized by wire.[read|write]Reflect
type Account struct {
Address []byte `json:"address"`
PubKey PubKey `json:"pub_key"`
Sequence int `json:"sequence"`
Balance int64 `json:"balance"`
Code []byte `json:"code"` // VM code
StorageRoot []byte `json:"storage_root"` // VM storage merkle root.
Permissions ptypes.AccountPermissions `json:"permissions"`
}
func (acc *Account) Copy() *Account {
accCopy := *acc
return &accCopy
}
func (acc *Account) String() string {
if acc == nil {
return "nil-Account"
}
return fmt.Sprintf("Account{%X:%v B:%v C:%v S:%X P:%s}", acc.Address, acc.PubKey, acc.Balance, len(acc.Code), acc.StorageRoot, acc.Permissions)
}
func AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) {
wire.WriteBinary(o.(*Account), w, n, err)
}
func AccountDecoder(r io.Reader, n *int64, err *error) interface{} {
return wire.ReadBinary(&Account{}, r, n, err)
}
var AccountCodec = wire.Codec{
Encode: AccountEncoder,
Decode: AccountDecoder,
}

+ 0
- 85
account/priv_account.go View File

@ -1,85 +0,0 @@
package account
import (
"github.com/tendermint/ed25519"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
)
type PrivAccount struct {
Address []byte `json:"address"`
PubKey PubKey `json:"pub_key"`
PrivKey PrivKey `json:"priv_key"`
}
func (pA *PrivAccount) Generate(index int) *PrivAccount {
newPrivKey := pA.PrivKey.(PrivKeyEd25519).Generate(index)
newPubKey := newPrivKey.PubKey()
newAddress := newPubKey.Address()
return &PrivAccount{
Address: newAddress,
PubKey: newPubKey,
PrivKey: newPrivKey,
}
}
func (pA *PrivAccount) Sign(chainID string, o Signable) Signature {
return pA.PrivKey.Sign(SignBytes(chainID, o))
}
func (pA *PrivAccount) String() string {
return Fmt("PrivAccount{%X}", pA.Address)
}
//----------------------------------------
// Generates a new account with private key.
func GenPrivAccount() *PrivAccount {
privKeyBytes := new([64]byte)
copy(privKeyBytes[:32], CRandBytes(32))
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes)
pubKey := PubKeyEd25519(*pubKeyBytes)
privKey := PrivKeyEd25519(*privKeyBytes)
return &PrivAccount{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
}
}
// Generates 32 priv key bytes from secret
func GenPrivKeyBytesFromSecret(secret string) []byte {
return wire.BinarySha256(secret) // Not Ripemd160 because we want 32 bytes.
}
// Generates a new account with private key from SHA256 hash of a secret
func GenPrivAccountFromSecret(secret string) *PrivAccount {
privKey32 := GenPrivKeyBytesFromSecret(secret)
privKeyBytes := new([64]byte)
copy(privKeyBytes[:32], privKey32)
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes)
pubKey := PubKeyEd25519(*pubKeyBytes)
privKey := PrivKeyEd25519(*privKeyBytes)
return &PrivAccount{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
}
}
func GenPrivAccountFromPrivKeyBytes(privKeyBytes []byte) *PrivAccount {
if len(privKeyBytes) != 64 {
PanicSanity(Fmt("Expected 64 bytes but got %v", len(privKeyBytes)))
}
var privKeyArray [64]byte
copy(privKeyArray[:], privKeyBytes)
pubKeyBytes := ed25519.MakePublicKey(&privKeyArray)
pubKey := PubKeyEd25519(*pubKeyBytes)
privKey := PrivKeyEd25519(privKeyArray)
return &PrivAccount{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
}
}

+ 0
- 70
account/priv_key.go View File

@ -1,70 +0,0 @@
package account
import (
"github.com/tendermint/ed25519"
"github.com/tendermint/ed25519/extra25519"
"github.com/tendermint/go-wire"
. "github.com/tendermint/go-common"
)
// PrivKey is part of PrivAccount and state.PrivValidator.
type PrivKey interface {
Sign(msg []byte) Signature
PubKey() PubKey
}
// Types of PrivKey implementations
const (
PrivKeyTypeEd25519 = byte(0x01)
)
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ PrivKey }{},
wire.ConcreteType{PrivKeyEd25519{}, PrivKeyTypeEd25519},
)
//-------------------------------------
// Implements PrivKey
type PrivKeyEd25519 [64]byte
func (key PrivKeyEd25519) Sign(msg []byte) Signature {
privKeyBytes := [64]byte(key)
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
return SignatureEd25519(*signatureBytes)
}
func (privKey PrivKeyEd25519) PubKey() PubKey {
privKeyBytes := [64]byte(privKey)
return PubKeyEd25519(*ed25519.MakePublicKey(&privKeyBytes))
}
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte {
keyCurve25519 := new([32]byte)
privKeyBytes := [64]byte(privKey)
extra25519.PrivateKeyToCurve25519(keyCurve25519, &privKeyBytes)
return keyCurve25519
}
func (privKey PrivKeyEd25519) String() string {
return Fmt("PrivKeyEd25519{*****}")
}
// Deterministically generates new priv-key bytes from key.
func (key PrivKeyEd25519) Generate(index int) PrivKeyEd25519 {
newBytes := wire.BinarySha256(struct {
PrivKey [64]byte
Index int
}{key, index})
var newKey [64]byte
copy(newKey[:], newBytes)
return PrivKeyEd25519(newKey)
}
func GenPrivKeyEd25519() PrivKeyEd25519 {
privKeyBytes := new([64]byte)
copy(privKeyBytes[:32], CRandBytes(32))
ed25519.MakePublicKey(privKeyBytes)
return PrivKeyEd25519(*privKeyBytes)
}

+ 0
- 91
account/pub_key.go View File

@ -1,91 +0,0 @@
package account
import (
"bytes"
"github.com/tendermint/ed25519"
"github.com/tendermint/ed25519/extra25519"
"golang.org/x/crypto/ripemd160"
"github.com/tendermint/go-wire"
. "github.com/tendermint/go-common"
)
// PubKey is part of Account and Validator.
type PubKey interface {
Address() []byte
VerifyBytes(msg []byte, sig Signature) bool
}
// Types of PubKey implementations
const (
PubKeyTypeEd25519 = byte(0x01)
)
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ PubKey }{},
wire.ConcreteType{PubKeyEd25519{}, PubKeyTypeEd25519},
)
//-------------------------------------
// Implements PubKey
type PubKeyEd25519 [32]byte
// TODO: Slicing the array gives us length prefixing but loses the type byte.
// Revisit if we add more pubkey types.
// For now, we artificially append the type byte in front to give us backwards
// compatibility for when the pubkey wasn't fixed length array
func (pubKey PubKeyEd25519) Address() []byte {
w, n, err := new(bytes.Buffer), new(int64), new(error)
wire.WriteBinary(pubKey[:], w, n, err)
if *err != nil {
PanicCrisis(*err)
}
// append type byte
encodedPubkey := append([]byte{1}, w.Bytes()...)
hasher := ripemd160.New()
hasher.Write(encodedPubkey) // does not error
return hasher.Sum(nil)
}
// TODO: Consider returning a reason for failure, or logging a runtime type mismatch.
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool {
sig, ok := sig_.(SignatureEd25519)
if !ok {
return false
}
pubKeyBytes := [32]byte(pubKey)
sigBytes := [64]byte(sig)
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes)
}
// For use with golang/crypto/nacl/box
// If error, returns nil.
func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte {
keyCurve25519, pubKeyBytes := new([32]byte), [32]byte(pubKey)
ok := extra25519.PublicKeyToCurve25519(keyCurve25519, &pubKeyBytes)
if !ok {
return nil
}
return keyCurve25519
}
func (pubKey PubKeyEd25519) String() string {
return Fmt("PubKeyEd25519{%X}", pubKey[:])
}
// Must return the full bytes in hex.
// Used for map keying, etc.
func (pubKey PubKeyEd25519) KeyString() string {
return Fmt("%X", pubKey[:])
}
func (pubKey PubKeyEd25519) Equals(other PubKey) bool {
if otherEd, ok := other.(PubKeyEd25519); ok {
return bytes.Equal(pubKey[:], otherEd[:])
} else {
return false
}
}

+ 0
- 34
account/signature.go View File

@ -1,34 +0,0 @@
package account
import (
"fmt"
"github.com/tendermint/go-wire"
. "github.com/tendermint/go-common"
)
// Signature is a part of Txs and consensus Votes.
type Signature interface {
IsZero() bool
String() string
}
// Types of Signature implementations
const (
SignatureTypeEd25519 = byte(0x01)
)
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ Signature }{},
wire.ConcreteType{SignatureEd25519{}, SignatureTypeEd25519},
)
//-------------------------------------
// Implements Signature
type SignatureEd25519 [64]byte
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 }
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) }

+ 0
- 71
account/signature_test.go View File

@ -1,71 +0,0 @@
package account
import (
"bytes"
"testing"
"github.com/tendermint/ed25519"
"github.com/tendermint/go-wire"
. "github.com/tendermint/go-common"
)
func TestSignAndValidate(t *testing.T) {
privAccount := GenPrivAccount()
pubKey := privAccount.PubKey
privKey := privAccount.PrivKey
msg := CRandBytes(128)
sig := privKey.Sign(msg)
t.Logf("msg: %X, sig: %X", msg, sig)
// Test the signature
if !pubKey.VerifyBytes(msg, sig) {
t.Errorf("Account message signature verification failed")
}
// Mutate the signature, just one bit.
sigEd := sig.(SignatureEd25519)
sigEd[0] ^= byte(0x01)
sig = Signature(sigEd)
if pubKey.VerifyBytes(msg, sig) {
t.Errorf("Account message signature verification should have failed but passed instead")
}
}
func TestBinaryDecode(t *testing.T) {
privAccount := GenPrivAccount()
pubKey := privAccount.PubKey
privKey := privAccount.PrivKey
msg := CRandBytes(128)
sig := privKey.Sign(msg)
t.Logf("msg: %X, sig: %X", msg, sig)
buf, n, err := new(bytes.Buffer), new(int64), new(error)
wire.WriteBinary(sig, buf, n, err)
if *err != nil {
t.Fatalf("Failed to write Signature: %v", err)
}
if len(buf.Bytes()) != ed25519.SignatureSize+1 {
// 1 byte TypeByte, 64 bytes signature bytes
t.Fatalf("Unexpected signature write size: %v", len(buf.Bytes()))
}
if buf.Bytes()[0] != SignatureTypeEd25519 {
t.Fatalf("Unexpected signature type byte")
}
sig2, ok := wire.ReadBinary(SignatureEd25519{}, buf, n, err).(SignatureEd25519)
if !ok || *err != nil {
t.Fatalf("Failed to read Signature: %v", err)
}
// Test the signature
if !pubKey.VerifyBytes(msg, sig2) {
t.Errorf("Account message signature verification failed")
}
}

+ 3
- 3
blockchain/reactor.go View File

@ -8,11 +8,11 @@ import (
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/events"
"github.com/tendermint/go-p2p"
"github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/events"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/go-wire"
)
const (
@ -217,7 +217,7 @@ FOR_LOOP:
firstParts := first.MakePartSet()
firstPartsHeader := firstParts.Header()
// Finally, verify the first block using the second's validation.
err := bcR.state.BondedValidators.VerifyValidation(
err := bcR.state.Validators.VerifyValidation(
bcR.state.ChainID, first.Hash(), firstPartsHeader, first.Height, second.LastValidation)
if err != nil {
log.Info("error in validation", "error", err)


+ 2
- 2
cmd/barak/types/command.go View File

@ -2,13 +2,13 @@
package types
import (
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
)
type AuthCommand struct {
CommandJSONStr string
Signatures []acm.Signature
Signatures []crypto.Signature
}
type NoncedCommand struct {


+ 2
- 2
cmd/barak/types/validator.go View File

@ -1,10 +1,10 @@
package types
import (
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
)
type Validator struct {
VotingPower int64
PubKey acm.PubKey
PubKey crypto.PubKey
}

+ 2
- 2
cmd/barak/validate.go View File

@ -1,11 +1,11 @@
package main
import (
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
. "github.com/tendermint/tendermint/cmd/barak/types"
)
func validate(signBytes []byte, validators []Validator, signatures []acm.Signature) bool {
func validate(signBytes []byte, validators []Validator, signatures []crypto.Signature) bool {
var signedPower int64
var totalPower int64
for i, val := range validators {


+ 17
- 17
cmd/debora/commands.go View File

@ -6,7 +6,7 @@ import (
"net/url"
"os"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
btypes "github.com/tendermint/tendermint/cmd/barak/types"
. "github.com/tendermint/go-common"
@ -20,57 +20,57 @@ import (
// and then it is broadcast).
// TODO: Implement a reasonable workflow with multiple validators.
func StartProcess(privKey acm.PrivKey, remote string, command btypes.CommandStartProcess) (response btypes.ResponseStartProcess, err error) {
func StartProcess(privKey crypto.PrivKey, remote string, command btypes.CommandStartProcess) (response btypes.ResponseStartProcess, err error) {
nonce, err := GetNonce(remote)
if err != nil {
return response, err
}
commandBytes, signature := SignCommand(privKey, nonce+1, command)
_, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response)
return response, err
}
func StopProcess(privKey acm.PrivKey, remote string, command btypes.CommandStopProcess) (response btypes.ResponseStopProcess, err error) {
func StopProcess(privKey crypto.PrivKey, remote string, command btypes.CommandStopProcess) (response btypes.ResponseStopProcess, err error) {
nonce, err := GetNonce(remote)
if err != nil {
return response, err
}
commandBytes, signature := SignCommand(privKey, nonce+1, command)
_, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response)
return response, err
}
func ListProcesses(privKey acm.PrivKey, remote string, command btypes.CommandListProcesses) (response btypes.ResponseListProcesses, err error) {
func ListProcesses(privKey crypto.PrivKey, remote string, command btypes.CommandListProcesses) (response btypes.ResponseListProcesses, err error) {
nonce, err := GetNonce(remote)
if err != nil {
return response, err
}
commandBytes, signature := SignCommand(privKey, nonce+1, command)
_, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response)
return response, err
}
func OpenListener(privKey acm.PrivKey, remote string, command btypes.CommandOpenListener) (response btypes.ResponseOpenListener, err error) {
func OpenListener(privKey crypto.PrivKey, remote string, command btypes.CommandOpenListener) (response btypes.ResponseOpenListener, err error) {
nonce, err := GetNonce(remote)
if err != nil {
return response, err
}
commandBytes, signature := SignCommand(privKey, nonce+1, command)
_, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response)
return response, err
}
func CloseListener(privKey acm.PrivKey, remote string, command btypes.CommandCloseListener) (response btypes.ResponseCloseListener, err error) {
func CloseListener(privKey crypto.PrivKey, remote string, command btypes.CommandCloseListener) (response btypes.ResponseCloseListener, err error) {
nonce, err := GetNonce(remote)
if err != nil {
return response, err
}
commandBytes, signature := SignCommand(privKey, nonce+1, command)
_, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response)
return response, err
}
func DownloadFile(privKey acm.PrivKey, remote string, command btypes.CommandServeFile, outPath string) (n int64, err error) {
func DownloadFile(privKey crypto.PrivKey, remote string, command btypes.CommandServeFile, outPath string) (n int64, err error) {
// Create authCommandJSONBytes
nonce, err := GetNonce(remote)
if err != nil {
@ -79,7 +79,7 @@ func DownloadFile(privKey acm.PrivKey, remote string, command btypes.CommandServ
commandBytes, signature := SignCommand(privKey, nonce+1, command)
authCommand := btypes.AuthCommand{
CommandJSONStr: string(commandBytes),
Signatures: []acm.Signature{signature},
Signatures: []crypto.Signature{signature},
}
authCommandJSONBytes := wire.JSONBytes(authCommand)
// Make request and write to outPath.
@ -100,13 +100,13 @@ func DownloadFile(privKey acm.PrivKey, remote string, command btypes.CommandServ
return n, nil
}
func Quit(privKey acm.PrivKey, remote string, command btypes.CommandQuit) (response btypes.ResponseQuit, err error) {
func Quit(privKey crypto.PrivKey, remote string, command btypes.CommandQuit) (response btypes.ResponseQuit, err error) {
nonce, err := GetNonce(remote)
if err != nil {
return response, err
}
commandBytes, signature := SignCommand(privKey, nonce+1, command)
_, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response)
return response, err
}
@ -128,7 +128,7 @@ func GetStatus(remote string) (response btypes.ResponseStatus, err error) {
}
// Each developer runs this
func SignCommand(privKey acm.PrivKey, nonce int64, command btypes.Command) ([]byte, acm.Signature) {
func SignCommand(privKey crypto.PrivKey, nonce int64, command btypes.Command) ([]byte, crypto.Signature) {
noncedCommand := btypes.NoncedCommand{
Nonce: nonce,
Command: command,
@ -139,7 +139,7 @@ func SignCommand(privKey acm.PrivKey, nonce int64, command btypes.Command) ([]by
}
// Somebody aggregates the signatures and calls this.
func RunAuthCommand(remote string, commandJSONBytes []byte, signatures []acm.Signature, dest interface{}) (interface{}, error) {
func RunAuthCommand(remote string, commandJSONBytes []byte, signatures []crypto.Signature, dest interface{}) (interface{}, error) {
authCommand := btypes.AuthCommand{
CommandJSONStr: string(commandJSONBytes),
Signatures: signatures,


+ 2
- 2
cmd/debora/main.go View File

@ -11,7 +11,7 @@ import (
"strings"
"sync"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
btypes "github.com/tendermint/tendermint/cmd/barak/types"
. "github.com/tendermint/go-common"
cfg "github.com/tendermint/go-config"
@ -29,7 +29,7 @@ func remoteNick(remote string) string {
var Config = struct {
Remotes []string
PrivKey acm.PrivKey
PrivKey crypto.PrivKey
}{}
func main() {


+ 0
- 10
cmd/sim_txs/README.md View File

@ -1,10 +0,0 @@
Simulate the Economy of Texas.
```bash
sim_txs --priv-key "PRIV_KEY_HEX" --num-accounts 1000 --remote localhost:46657
```
The above uses the rpc-host to fetch the account state for the account with given priv-key,
then deterministically generates num-accounts more accounts to spread the coins around.
It's a test utility.

+ 0
- 169
cmd/sim_txs/main.go View File

@ -1,169 +0,0 @@
package main
import (
"encoding/hex"
"flag"
"fmt"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
cclient "github.com/tendermint/tendermint/rpc/core_client"
"github.com/tendermint/tendermint/types"
)
const Version = "0.0.1"
const sleepSeconds = 1 // Every second
// Parse command-line options
func parseFlags() (privKeyHex string, numAccounts int, remote string) {
var version bool
flag.StringVar(&privKeyHex, "priv-key", "", "Private key bytes in HEX")
flag.IntVar(&numAccounts, "num-accounts", 1000, "Deterministically generates this many sub-accounts")
flag.StringVar(&remote, "remote", "localhost:46657", "Remote RPC host:port")
flag.BoolVar(&version, "version", false, "Version")
flag.Parse()
if version {
Exit(Fmt("sim_txs version %v", Version))
}
return
}
func main() {
// Read options
privKeyHex, numAccounts, remote := parseFlags()
// Print args.
// fmt.Println(privKeyHex, numAccounts, remote)
privKeyBytes, err := hex.DecodeString(privKeyHex)
if err != nil {
panic(err)
}
root := acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes)
fmt.Println("Computed address: %X", root.Address)
// Get root account.
rootAccount, err := getAccount(remote, root.Address)
if err != nil {
fmt.Println(Fmt("Root account %X does not exist: %v", root.Address, err))
return
} else {
fmt.Println("Root account", rootAccount)
}
// Load all accounts
accounts := make([]*acm.Account, numAccounts+1)
accounts[0] = rootAccount
privAccounts := make([]*acm.PrivAccount, numAccounts+1)
privAccounts[0] = root
for i := 1; i < numAccounts+1; i++ {
privAccounts[i] = root.Generate(i)
account, err := getAccount(remote, privAccounts[i].Address)
if err != nil {
fmt.Println("Error", err)
return
} else {
accounts[i] = account
}
}
// Test: send from root to accounts[1]
sendTx := makeRandomTransaction(10, rootAccount.Sequence+1, root, 2, accounts)
fmt.Println(sendTx)
wsClient := cclient.NewWSClient("ws://" + remote + "/websocket")
_, err = wsClient.Start()
if err != nil {
Exit(Fmt("Failed to establish websocket connection: %v", err))
}
wsClient.Subscribe(types.EventStringAccInput(sendTx.Inputs[0].Address))
go func() {
for {
foo := <-wsClient.EventsCh
fmt.Println("!!", foo)
}
}()
err = broadcastSendTx(remote, sendTx)
if err != nil {
Exit(Fmt("Failed to broadcast SendTx: %v", err))
return
}
// Trap signal
TrapSignal(func() {
fmt.Println("sim_txs shutting down")
})
}
func getAccount(remote string, address []byte) (*acm.Account, error) {
// var account *acm.Account = new(acm.Account)
account, err := rpcclient.Call("http://"+remote, "get_account", []interface{}{address}, (*acm.Account)(nil))
if err != nil {
return nil, err
}
if account.(*acm.Account) == nil {
return &acm.Account{Address: address}, nil
} else {
return account.(*acm.Account), nil
}
}
func broadcastSendTx(remote string, sendTx *types.SendTx) error {
receipt, err := rpcclient.Call("http://"+remote, "broadcast_tx", []interface{}{sendTx}, (*ctypes.Receipt)(nil))
if err != nil {
return err
}
fmt.Println("Broadcast receipt:", receipt)
return nil
}
// Make a random send transaction from srcIndex to N other accounts.
// balance: balance to send from input
// sequence: sequence to sign with
// inputPriv: input privAccount
func makeRandomTransaction(balance int64, sequence int, inputPriv *acm.PrivAccount, sendCount int, accounts []*acm.Account) *types.SendTx {
if sendCount >= len(accounts) {
PanicSanity("Cannot make tx with sendCount >= len(accounts)")
}
// Remember which accounts were chosen
accMap := map[string]struct{}{}
accMap[string(inputPriv.Address)] = struct{}{}
// Find a selection of accounts to send to
outputs := []*acm.Account{}
for i := 0; i < sendCount; i++ {
for {
idx := RandInt() % len(accounts)
account := accounts[idx]
if _, ok := accMap[string(account.Address)]; ok {
continue
}
accMap[string(account.Address)] = struct{}{}
outputs = append(outputs, account)
break
}
}
// Construct SendTx
sendTx := types.NewSendTx()
err := sendTx.AddInputWithNonce(inputPriv.PubKey, balance, sequence)
if err != nil {
panic(err)
}
for _, output := range outputs {
sendTx.AddOutput(output.Address, balance/int64(len(outputs)))
}
// Sign SendTx
sendTx.SignInput("tendermint_testnet_9", 0, inputPriv)
return sendTx
}

+ 0
- 43
cmd/tendermint/gen_account.go View File

@ -1,43 +0,0 @@
package main
import (
"fmt"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
)
func gen_account() {
secret, err := Prompt(`Enter your desired secret, or just hit <Enter> to generate a random account.
IMPORTANT: If you don't know what a dictionary attack is, just hit Enter
> `, "")
if err != nil {
Exit(Fmt("Not sure what happened: %v", err))
}
if secret == "" {
privAccount := acm.GenPrivAccount()
privAccountJSONBytes := wire.JSONBytes(privAccount)
fmt.Printf(`Generated a new random account!
%v
`,
string(privAccountJSONBytes),
)
} else {
privAccount := acm.GenPrivAccountFromSecret(secret)
privAccountJSONBytes := wire.JSONBytes(privAccount)
fmt.Printf(`Generated a new account from secret: [%v]!
%v
`,
secret,
string(privAccountJSONBytes),
)
}
}

+ 0
- 42
cmd/tendermint/get_account.go View File

@ -1,42 +0,0 @@
package main
import (
"encoding/hex"
"fmt"
. "github.com/tendermint/go-common"
cclient "github.com/tendermint/tendermint/rpc/core_client"
)
func get_account() {
addrHex, err := Prompt("Enter the address of the account in HEX (e.g. 9FCBA7F840A0BFEBBE755E853C9947270A912D04):\n> ", "")
if err != nil {
Exit(Fmt("Error: %v", err))
}
cli := cclient.NewClient("http://localhost:46657", "JSONRPC")
address, err := hex.DecodeString(addrHex)
if err != nil {
Exit(Fmt("Address was not hex: %v", addrHex))
}
res, err := cli.GetAccount(address)
if res == nil {
Exit(Fmt("Account does not exist: %X", address))
}
if err != nil {
Exit(Fmt("Error fetching account: %v", err))
}
acc := res.Account
fmt.Printf(`
Address: %X
PubKey: %v
Sequence: %v
Balance: %v
Permissions: %v
`,
acc.Address,
acc.PubKey,
acc.Sequence,
acc.Balance,
acc.Permissions)
}

+ 0
- 9
cmd/tendermint/main.go View File

@ -19,10 +19,7 @@ func main() {
Commands:
node Run the tendermint node
show_validator Show this node's validator info
gen_account Generate new account keypair
gen_validator Generate new validator keypair
get_account Get account balance
send_tx Sign and publish a SendTx
probe_upnp Test UPnP functionality
version Show version info
`)
@ -39,14 +36,8 @@ Commands:
node.RunNode()
case "show_validator":
show_validator()
case "gen_account":
gen_account()
case "gen_validator":
gen_validator()
case "get_account":
get_account()
case "send_tx":
send_tx()
case "probe_upnp":
probe_upnp()
case "unsafe_reset_priv_validator":


+ 0
- 120
cmd/tendermint/send_tx.go View File

@ -1,120 +0,0 @@
package main
import (
"encoding/hex"
"fmt"
"strconv"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
cclient "github.com/tendermint/tendermint/rpc/core_client"
"github.com/tendermint/tendermint/types"
)
func getString(prompt string) string {
input, err := Prompt(prompt, "")
if err != nil {
Exit(Fmt("Error reading input: %v", err))
}
return input
}
func getByteSliceFromHex(prompt string) []byte {
input := getString(prompt)
bytes, err := hex.DecodeString(input)
if err != nil {
Exit(Fmt("Not in hex format: %v\nError: %v\n", input, err))
}
return bytes
}
func getInt(prompt string) int {
input := getString(prompt)
i, err := strconv.Atoi(input)
if err != nil {
Exit(Fmt("Not a valid int64 amount: %v\nError: %v\n", input, err))
}
return i
}
func getInt64(prompt string) int64 {
return int64(getInt(prompt))
}
func send_tx() {
// Get PrivAccount
var privAccount *acm.PrivAccount
secret := getString("Enter your secret, or just hit <Enter> to enter a private key in HEX.\n> ")
if secret == "" {
privKeyBytes := getByteSliceFromHex("Enter your private key in HEX (e.g. E353CAD81134A301A542AEBE2D2E4EF1A64A145117EC72743AE9C9D171A4AA69F3A7DD670A9E9307AAED000D97D5B3C07D90276BFCEEDA5ED11DA089A4E87A81):\n> ")
privAccount = acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes)
} else {
// Auto-detect private key hex
if len(secret) == 128 {
privKeyBytes, err := hex.DecodeString(secret)
if err == nil {
fmt.Println("Detected priv-key bytes...")
privAccount = acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes)
} else {
fmt.Println("That's a long seed...")
privAccount = acm.GenPrivAccountFromSecret(secret)
}
} else {
privAccount = acm.GenPrivAccountFromSecret(secret)
}
}
pubKey := privAccount.PubKey
// Get account data
cli := cclient.NewClient("http://localhost:46657", "JSONRPC")
res, err := cli.GetAccount(privAccount.Address)
if err != nil {
Exit(Fmt("Error fetching account: %v", err))
}
if res == nil {
Exit(Fmt("No account was found with that secret/private-key"))
}
inputAcc := res.Account
fmt.Printf(`
Source account:
Address: %X
PubKey: %v
Sequence: %v
Balance: %v
Permissions: %v
`,
inputAcc.Address,
pubKey,
inputAcc.Sequence,
inputAcc.Balance,
inputAcc.Permissions)
output := getByteSliceFromHex("\nEnter the output address in HEX:\n> ")
amount := getInt64("Enter the amount to send:\n> ")
// Construct transaction
tx := types.NewSendTx()
tx.AddInputWithNonce(pubKey, amount, inputAcc.Sequence+1)
tx.AddOutput(output, amount)
tx.Inputs[0].Signature = privAccount.Sign(config.GetString("chain_id"), tx)
fmt.Println("Signed SendTx!: ", tx)
// Sign up for events
wsCli := cclient.NewWSClient("ws://localhost:46657/websocket")
wsCli.Start()
err = wsCli.Subscribe(types.EventStringAccInput(inputAcc.Address))
if err != nil {
Exit(Fmt("Error subscribing to account send event: %v", err))
}
// Broadcast transaction
_, err = cli.BroadcastTx(tx)
if err != nil {
Exit(Fmt("Error broadcasting transaction: %v", err))
}
fmt.Println("Waiting for confirmation...")
_ = <-wsCli.EventsCh
fmt.Println("Confirmed.")
}

+ 0
- 1
config/tendermint/config.go View File

@ -66,7 +66,6 @@ func GetConfig(rootDir string) cfg.Config {
mapConfig.SetDefault("rpc_laddr", "0.0.0.0:46657")
mapConfig.SetDefault("prof_laddr", "")
mapConfig.SetDefault("revision_file", rootDir+"/revision")
mapConfig.SetDefault("local_routing", false)
return mapConfig
}


+ 0
- 2
config/tendermint_test/config.go View File

@ -1,4 +1,3 @@
// Import this in all *_test.go files to initialize ~/.tendermint_test.
package tendermint_test
@ -72,7 +71,6 @@ func GetConfig(rootDir string) cfg.Config {
mapConfig.SetDefault("rpc_laddr", "0.0.0.0:36657")
mapConfig.SetDefault("prof_laddr", "")
mapConfig.SetDefault("revision_file", rootDir+"/revision")
mapConfig.SetDefault("local_routing", false)
return mapConfig
}


+ 10
- 186
consensus/state.go View File

@ -1,153 +1,3 @@
/*
* Terms:
NewHeight, NewRound, Propose, Prevote, Precommit represent state machine steps. (aka RoundStep).
To "prevote/precommit" something means to broadcast a prevote/precommit vote for something.
(H,R) means a particular height H and round R. A vote "at (H,R)" is a vote signed with (H,R).
* Proposals:
A proposal is signed and published by the designated proposer at each round.
A proposal at (H,R) is composed of a proposed block of height H, and optionally a POL round number.
The POL round number R' (where R' < R) is set to the latest POL round known to the proposer.
If the proposer is locked on a block, it must include a POL round for the proposal.
* POL and Justification of votes:
A set of +2/3 of prevotes for a particular block or <nil> at (H,R) is called a POL (proof-of-lock).
A POL for <nil> might instead be called a proof-of-unlock, but it's better to have a single terminology for both.
Each precommit which changes the lock at round R must be justified by a POL
where +2/3 prevoted for some block or <nil> at some round, equal to or less than R,
but greater than the last round at which the lock was changed.
POL = Proof-of-Lock = +2/3 prevotes for block B (or +2/3 prevotes for <nil>) at (H,R)
lastLockChangeRound < POLRound <= newLockChangeRound
Without the POLRound <= newLockChangeRound condition, an unlock would be possible from a
future condition that hasn't happened yet, so it destroys deterministic accountability.
The point of the above inequality is to ensure that changes in the lock (locking/unlocking/lock-changing)
are always justified by something that happened "in the past" by round numbers, so if there is a problem,
we can deterministically figure out "when it was caused" and by who.
If there is a blockchain halt or fork, the blame will fall on +1/3 of Byzantine voting power =
who cannot push the blame into earlier rounds. (See lemma 4).
* Block commits:
The set of +2/3 of precommits at the same round for the same block is called a commit.
A block contains the last block's commit which is comprised of +2/3 precommit votes at (H-1,R).
While all the precommits in the commit are from the same height & round (ordered by validator index),
some precommits may be absent (e.g. if the validator's precommit vote didn't reach the proposer in time),
or some precommits may be for different blockhashes for the last block hash (which is fine).
* Consensus State Machine Overview:
During NewHeight/NewRound/Propose/Prevote/Precommit:
* Nodes gossip the proposal block proposed by the designated proposer at round.
* Nodes gossip prevotes/precommits at rounds [0...currentRound+1] (currentRound+1 to allow round-skipping)
* Nodes gossip prevotes for the proposal's POL (proof-of-lock) round if proposed.
* Nodes gossip to late nodes (lagging in height) with precommits of the commit round (aka catchup)
Upon each state transition, the height/round/step is broadcast to neighboring peers.
* NewRound(height:H,round:R):
* Set up new round. --> goto Propose(H,R)
* NOTE: Not much happens in this step. It exists for clarity.
* Propose(height:H,round:R):
* Upon entering Propose:
* The designated proposer proposes a block at (H,R).
* The Propose step ends:
* After `timeoutPropose` after entering Propose. --> goto Prevote(H,R)
* After receiving proposal block and all POL prevotes. --> goto Prevote(H,R)
* After any +2/3 prevotes received at (H,R+1). --> goto Prevote(H,R+1)
* After any +2/3 precommits received at (H,R+1). --> goto Precommit(H,R+1)
* After +2/3 precommits received for a particular block. --> goto Commit(H)
* Prevote(height:H,round:R):
* Upon entering Prevote, each validator broadcasts its prevote vote.
* If the validator is locked on a block, it prevotes that.
* Else, if the proposed block from Propose(H,R) is good, it prevotes that.
* Else, if the proposal is invalid or wasn't received on time, it prevotes <nil>.
* The Prevote step ends:
* After +2/3 prevotes for a particular block or <nil>. --> goto Precommit(H,R)
* After `timeoutPrevote` after receiving any +2/3 prevotes. --> goto Precommit(H,R)
* After any +2/3 prevotes received at (H,R+1). --> goto Prevote(H,R+1)
* After any +2/3 precommits received at (H,R+1). --> goto Precommit(H,R+1)
* After +2/3 precommits received for a particular block. --> goto Commit(H)
* Precommit(height:H,round:R):
* Upon entering Precommit, each validator broadcasts its precommit vote.
* If the validator had seen +2/3 of prevotes for a particular block from Prevote(H,R),
it locks (changes lock to) that block and precommits that block.
* Else, if the validator had seen +2/3 of prevotes for <nil>, it unlocks and precommits <nil>.
* Else, if +2/3 of prevotes for a particular block or <nil> is not received on time,
it precommits <nil>.
* The Precommit step ends:
* After +2/3 precommits for a particular block. --> goto Commit(H)
* After +2/3 precommits for <nil>. --> goto NewRound(H,R+1)
* After `timeoutPrecommit` after receiving any +2/3 precommits. --> goto NewRound(H,R+1)
* After any +2/3 prevotes received at (H,R+1). --> goto Prevote(H,R+1)
* After any +2/3 precommits received at (H,R+1). --> goto Precommit(H,R+1)
* Commit(height:H):
* Set CommitTime = now
* Wait until block is received. --> goto NewHeight(H+1)
* NewHeight(height:H):
* Move Precommits to LastCommit and increment height.
* Set StartTime = CommitTime+timeoutCommit
* Wait until `StartTime` to receive straggler commits. --> goto NewRound(H,0)
* Proof of Safety:
If a good validator commits at round R, it's because it saw +2/3 of precommits at round R.
This implies that (assuming tolerance bounds) +1/3 of honest nodes are still locked at round R+1.
These locked validators will remain locked until they see +2/3 prevote for something
else, but this won't happen because +1/3 are locked and honest.
* Proof of Liveness:
Lemma 1: If +1/3 good nodes are locked on two different blocks, the proposers' POLRound will
eventually cause nodes locked from the earlier round to unlock.
-> `timeoutProposalR` increments with round R, while the block.size && POL prevote size
are fixed, so eventually we'll be able to "fully gossip" the block & POL.
TODO: cap the block.size at something reasonable.
Lemma 2: If a good node is at round R, neighboring good nodes will soon catch up to round R.
Lemma 3: If a node at (H,R) receives +2/3 prevotes for a block (or +2/3 for <nil>) at (H,R+1),
it will enter NewRound(H,R+1).
Lemma 4: Terminal conditions imply the existence of deterministic accountability, for
a synchronous (fixed-duration) protocol extension (judgement).
TODO: define terminal conditions (fork and non-decision).
TODO: define new assumptions for the synchronous judgement period.
+-------------------------------------+
v |(Wait til `CommmitTime+timeoutCommit`)
+-----------+ +-----+-----+
+----------> | Propose +--------------+ | NewHeight |
| +-----------+ | +-----------+
| | ^
|(Else, after timeoutPrecommit) v |
+-----+-----+ +-----------+ |
| Precommit | <------------------------+ Prevote | |
+-----+-----+ +-----------+ |
|(When +2/3 Precommits for block found) |
v |
+--------------------------------------------------------------------+
| Commit |
| |
| * Set CommitTime = now; |
| * Wait for block, then stage/save/commit block; |
+--------------------------------------------------------------------+
*/
package consensus
import (
@ -157,14 +7,13 @@ import (
"sync"
"time"
acm "github.com/tendermint/tendermint/account"
bc "github.com/tendermint/tendermint/blockchain"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
bc "github.com/tendermint/tendermint/blockchain"
"github.com/tendermint/tendermint/events"
mempl "github.com/tendermint/tendermint/mempool"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/go-wire"
)
var (
@ -328,7 +177,6 @@ func NewConsensusState(state *sm.State, blockStore *bc.BlockStore, mempoolReacto
cs.updateToState(state)
// Don't call scheduleRound0 yet.
// We do that upon Start().
cs.maybeRebond()
cs.reconstructLastCommit(state)
cs.BaseService = *NewBaseService(log, "ConsensusState", cs)
return cs
@ -340,7 +188,7 @@ func (cs *ConsensusState) reconstructLastCommit(state *sm.State) {
if state.LastBlockHeight == 0 {
return
}
lastPrecommits := types.NewVoteSet(state.LastBlockHeight, 0, types.VoteTypePrecommit, state.LastBondedValidators)
lastPrecommits := types.NewVoteSet(state.LastBlockHeight, 0, types.VoteTypePrecommit, state.LastValidators)
seenValidation := cs.blockStore.LoadSeenValidation(state.LastBlockHeight)
for idx, precommit := range seenValidation.Precommits {
if precommit == nil {
@ -425,7 +273,7 @@ func (cs *ConsensusState) updateToState(state *sm.State) {
}
// Reset fields based on state.
validators := state.BondedValidators
validators := state.Validators
height := state.LastBlockHeight + 1 // next desired block height
lastPrecommits := (*types.VoteSet)(nil)
if cs.CommitRound > -1 && cs.Votes != nil {
@ -460,7 +308,7 @@ func (cs *ConsensusState) updateToState(state *sm.State) {
cs.Votes = NewHeightVoteSet(height, validators)
cs.CommitRound = -1
cs.LastCommit = lastPrecommits
cs.LastValidators = state.LastBondedValidators
cs.LastValidators = state.LastValidators
cs.state = state
cs.stagedBlock = nil
@ -470,30 +318,6 @@ func (cs *ConsensusState) updateToState(state *sm.State) {
cs.newStepCh <- cs.getRoundState()
}
// If we're unbonded, broadcast RebondTx.
func (cs *ConsensusState) maybeRebond() {
if cs.privValidator == nil || !cs.state.UnbondingValidators.HasAddress(cs.privValidator.Address) {
return
}
rebondTx := &types.RebondTx{
Address: cs.privValidator.Address,
Height: cs.Height,
}
err := cs.privValidator.SignRebondTx(cs.state.ChainID, rebondTx)
if err == nil {
err := cs.mempoolReactor.BroadcastTx(rebondTx)
if err != nil {
log.Error("Failed to broadcast RebondTx",
"height", cs.Height, "round", cs.Round, "tx", rebondTx, "error", err)
} else {
log.Notice("Signed and broadcast RebondTx",
"height", cs.Height, "round", cs.Round, "tx", rebondTx)
}
} else {
log.Warn("Error signing RebondTx", "height", cs.Height, "round", cs.Round, "tx", rebondTx, "error", err)
}
}
func (cs *ConsensusState) SetPrivValidator(priv *types.PrivValidator) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
@ -1011,8 +835,6 @@ func (cs *ConsensusState) FinalizeCommit(height int) {
// cs.StartTime is already set.
// Schedule Round0 to start soon.
go cs.scheduleRound0(height + 1)
// If we're unbonded, broadcast RebondTx.
cs.maybeRebond()
// By here,
// * cs.Height has been increment to height+1
@ -1049,7 +871,7 @@ func (cs *ConsensusState) SetProposal(proposal *types.Proposal) error {
}
// Verify signature
if !cs.Validators.Proposer().PubKey.VerifyBytes(acm.SignBytes(cs.state.ChainID, proposal), proposal.Signature) {
if !cs.Validators.Proposer().PubKey.VerifyBytes(types.SignBytes(cs.state.ChainID, proposal), proposal.Signature) {
return ErrInvalidProposalSignature
}
@ -1098,21 +920,23 @@ func (cs *ConsensusState) AddProposalBlockPart(height int, part *types.Part) (ad
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
func (cs *ConsensusState) TryAddVote(valIndex int, vote *types.Vote, peerKey string) (bool, error) {
added, address, err := cs.AddVote(valIndex, vote, peerKey)
added, _, err := cs.AddVote(valIndex, vote, peerKey)
if err != nil {
// If the vote height is off, we'll just ignore it,
// But if it's a conflicting sig, broadcast evidence tx for slashing.
// If it's otherwise invalid, punish peer.
if err == ErrVoteHeightMismatch {
return added, err
} else if errDupe, ok := err.(*types.ErrVoteConflictingSignature); ok {
} else if _, ok := err.(*types.ErrVoteConflictingSignature); ok {
log.Warn("Found conflicting vote. Publish evidence")
/* XXX
evidenceTx := &types.DupeoutTx{
Address: address,
VoteA: *errDupe.VoteA,
VoteB: *errDupe.VoteB,
}
cs.mempoolReactor.BroadcastTx(evidenceTx) // shouldn't need to check returned err
*/
return added, err
} else {
// Probably an invalid signature. Bad peer.


+ 2
- 32
consensus/state_test.go View File

@ -1,4 +1,3 @@
package consensus
import (
@ -1032,21 +1031,7 @@ func TestSlashingPrevotes(t *testing.T) {
// add the conflicting vote
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
// conflicting vote should cause us to broadcast dupeout tx on mempool
txs := cs1.mempoolReactor.Mempool.GetProposalTxs()
if len(txs) != 1 {
t.Fatal("expected to find a transaction in the mempool after double signing")
}
dupeoutTx, ok := txs[0].(*types.DupeoutTx)
if !ok {
t.Fatal("expected to find DupeoutTx in mempool after double signing")
}
if !bytes.Equal(dupeoutTx.Address, cs2.privValidator.Address) {
t.Fatalf("expected DupeoutTx for %X, got %X", cs2.privValidator.Address, dupeoutTx.Address)
}
// TODO: validate the sig
// XXX: Check for existence of Dupeout info
}
func TestSlashingPrecommits(t *testing.T) {
@ -1079,22 +1064,7 @@ func TestSlashingPrecommits(t *testing.T) {
// add precommit from cs2
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
// conflicting vote should cause us to broadcast dupeout tx on mempool
txs := cs1.mempoolReactor.Mempool.GetProposalTxs()
if len(txs) != 1 {
t.Fatal("expected to find a transaction in the mempool after double signing")
}
dupeoutTx, ok := txs[0].(*types.DupeoutTx)
if !ok {
t.Fatal("expected to find DupeoutTx in mempool after double signing")
}
if !bytes.Equal(dupeoutTx.Address, cs2.privValidator.Address) {
t.Fatalf("expected DupeoutTx for %X, got %X", cs2.privValidator.Address, dupeoutTx.Address)
}
// TODO: validate the sig
// XXX: Check for existence of Dupeout info
}
//------------------------------------------------------------------------------------------


+ 5
- 6
consensus/test.go View File

@ -6,11 +6,11 @@ import (
"testing"
"time"
bc "github.com/tendermint/tendermint/blockchain"
dbm "github.com/tendermint/go-db"
"github.com/tendermint/go-p2p"
bc "github.com/tendermint/tendermint/blockchain"
"github.com/tendermint/tendermint/events"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/go-p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@ -186,10 +186,9 @@ func validatePrevoteAndPrecommit(t *testing.T, cs *ConsensusState, thisRound, lo
func simpleConsensusState(nValidators int) ([]*ConsensusState, []*types.PrivValidator) {
// Get State
state, privAccs, privVals := sm.RandGenesisState(10, true, 1000, nValidators, false, 10)
_, _ = privAccs, privVals
state, privVals := sm.RandGenesisState(nValidators, false, 10)
fmt.Println(state.BondedValidators)
fmt.Println(state.Validators)
css := make([]*ConsensusState, nValidators)
for i := 0; i < nValidators; i++ {
@ -220,7 +219,7 @@ func simpleConsensusState(nValidators int) ([]*ConsensusState, []*types.PrivVali
}
func randConsensusState() (*ConsensusState, []*types.PrivValidator) {
state, _, privValidators := sm.RandGenesisState(20, false, 1000, 10, false, 1000)
state, privValidators := sm.RandGenesisState(10, false, 1000)
blockStore := bc.NewBlockStore(dbm.NewMemDB())
mempool := mempl.NewMempool(state)
mempoolReactor := mempl.NewMempoolReactor(mempool)


+ 0
- 312
crawler/crawl.go View File

@ -1,312 +0,0 @@
package crawler
import (
"fmt"
"time"
. "github.com/tendermint/go-common"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
cclient "github.com/tendermint/tendermint/rpc/core_client"
"github.com/tendermint/tendermint/types"
)
const (
CheckQueueBufferSize = 100
NodeQueueBufferSize = 100
GetPeersTickerSeconds = 5
)
//---------------------------------------------------------------------------------------
// crawler.Node
// A node is a peer on the network
type Node struct {
Host string
P2PPort uint16
RPCPort uint16
failed int
connected bool
client *NodeClient
LastSeen time.Time
ChainID string
BlockHeight int
BlockHistory map[int]time.Time // when we saw each block
NetInfo *ctypes.ResultNetInfo
Validator bool
// other peers we heard about this peer from
heardFrom map[string]struct{}
}
func (n *Node) Address() string {
return fmt.Sprintf("%s:%d", n.Host, n.RPCPort)
}
// Set the basic status and chain_id info for a node from RPC responses
func (n *Node) SetInfo(status *ctypes.ResultStatus, netinfo *ctypes.ResultNetInfo) {
n.LastSeen = time.Now()
n.ChainID = status.NodeInfo.ChainID
n.BlockHeight = status.LatestBlockHeight
n.NetInfo = netinfo
// n.Validator
}
// A node client is used to talk to a node over rpc and websockets
type NodeClient struct {
rpc cclient.Client
ws *cclient.WSClient
}
// Create a new client for the node at the given addr
func NewNodeClient(addr string) *NodeClient {
return &NodeClient{
rpc: cclient.NewClient("http://"+addr, "JSONRPC"),
ws: cclient.NewWSClient("ws://" + addr + "/events"),
}
}
// A simple wrapper for mediating access to the maps
type nodeInfo struct {
host string // the new nodes address
port uint16 // node's listening port
from string // the peer that told us about this node
connected bool // move node from nodePool to nodes
disconnected bool // move node from nodes to nodePool
}
func (ni nodeInfo) unpack() (string, uint16, string, bool, bool) {
return ni.host, ni.port, ni.from, ni.connected, ni.disconnected
}
// crawler.Node
//---------------------------------------------------------------------------------------
// crawler.Crawler
// A crawler has a local node, a set of potential nodes in the nodePool, and connected nodes.
// Maps are only accessed by one go-routine, mediated by the checkQueue
type Crawler struct {
QuitService
self *Node
client *NodeClient
checkQueue chan nodeInfo
nodePool map[string]*Node
nodes map[string]*Node
nodeQueue chan *Node
}
// Create a new Crawler using the local RPC server at addr
func NewCrawler(host string, port uint16) *Crawler {
crawler := &Crawler{
self: &Node{Host: host, RPCPort: port, client: NewNodeClient(fmt.Sprintf("%s:%d", host, port))},
checkQueue: make(chan nodeInfo, CheckQueueBufferSize),
nodePool: make(map[string]*Node),
nodes: make(map[string]*Node),
nodeQueue: make(chan *Node, NodeQueueBufferSize),
}
crawler.QuitService = *NewQuitService(log, "Crawler", crawler)
return crawler
}
func (c *Crawler) OnStart() error {
// connect to local node first, set info,
// and fire peers onto the checkQueue
if err := c.pollNode(c.self); err != nil {
return err
}
// connect to weboscket, subscribe to local events
// and run the read loop to listen for new blocks
_, err := c.self.client.ws.Start()
if err != nil {
return err
}
if err := c.self.client.ws.Subscribe(types.EventStringNewBlock()); err != nil {
return err
}
go c.readLoop(c.self)
// add ourselves to the nodes list
c.nodes[c.self.Address()] = c.self
// nodes we hear about get put on the checkQueue
// by pollNode and are handled in the checkLoop.
// if its a node we're not already connected to,
// it gets put on the nodeQueue and
// we attempt to connect in the connectLoop
go c.checkLoop()
go c.connectLoop()
return nil
}
// listen for events from the node and ping it for peers on a ticker
func (c *Crawler) readLoop(node *Node) {
eventsCh := node.client.ws.EventsCh
getPeersTicker := time.Tick(time.Second * GetPeersTickerSeconds)
for {
select {
case eventMsg := <-eventsCh:
// update the node with his new info
if err := c.consumeMessage(eventMsg, node); err != nil {
// lost the node, put him back on the checkQueu
c.checkNode(nodeInfo{
host: node.Host,
port: node.RPCPort,
disconnected: true,
})
}
case <-getPeersTicker:
if err := c.pollNode(node); err != nil {
// lost the node, put him back on the checkQueu
c.checkNode(nodeInfo{
host: node.Host,
port: node.RPCPort,
disconnected: true,
})
}
case <-c.Quit:
return
}
}
}
func (c *Crawler) consumeMessage(eventMsg ctypes.ResultEvent, node *Node) error {
block := eventMsg.Data.(*types.EventDataNewBlock).Block
node.LastSeen = time.Now()
node.BlockHeight = block.Height
node.BlockHistory[block.Height] = node.LastSeen
return nil
}
// check nodes against the nodePool map one at a time
// acts as a mutex on nodePool and nodes
func (c *Crawler) checkLoop() {
for {
select {
case ni := <-c.checkQueue:
host, port, from, connected, disconnected := ni.unpack()
addr := fmt.Sprintf("%s:%d", host, port)
// check if we need to swap node between maps (eg. its connected or disconnected)
// NOTE: once we hear about a node, we never forget ...
if connected {
n, _ := c.nodePool[addr]
c.nodes[addr] = n
delete(c.nodePool, addr)
continue
} else if disconnected {
n, _ := c.nodes[addr]
c.nodePool[addr] = n
delete(c.nodes, addr)
continue
}
// TODO: if address is badly formed
// we should punish ni.from
_ = from
n, ok := c.nodePool[addr]
// create the node if unknown
if !ok {
n = &Node{Host: host, RPCPort: port}
c.nodePool[addr] = n
} else if n.connected {
// should be removed soon
continue
}
// queue it for connecting to
c.nodeQueue <- n
case <-c.Quit:
return
}
}
}
// read off the nodeQueue and attempt to connect to nodes
func (c *Crawler) connectLoop() {
for {
select {
case node := <-c.nodeQueue:
go c.connectToNode(node)
case <-c.Quit:
// close all connections
for addr, node := range c.nodes {
_, _ = addr, node
// TODO: close conn
}
return
}
}
}
func (c *Crawler) connectToNode(node *Node) {
addr := node.Address()
node.client = NewNodeClient(addr)
_, err := node.client.ws.Start()
if err != nil {
fmt.Println("err on ws start:", err)
// set failed, return
}
// remove from nodePool, add to nodes
c.checkNode(nodeInfo{
host: node.Host,
port: node.RPCPort,
connected: true,
})
if err := c.pollNode(node); err != nil {
// TODO: we had a good ws con
// but failed on rpc?!
// try again or something ...
// if we still fail, report and disconnect
}
fmt.Println("Successfully connected to node", node.Address())
// blocks (until quit or err)
c.readLoop(node)
}
func (c *Crawler) checkNode(ni nodeInfo) {
c.checkQueue <- ni
}
func (c *Crawler) pollNode(node *Node) error {
// get the status info
status, err := node.client.rpc.Status()
if err != nil {
return err
}
// get peers and net info
netinfo, err := node.client.rpc.NetInfo()
if err != nil {
return err
}
// set the info for the node
node.SetInfo(status, netinfo)
// fire each peer on the checkQueue
for _, p := range netinfo.Peers {
c.checkNode(nodeInfo{
host: p.Host,
port: p.RPCPort,
from: node.Address(),
})
}
return nil
}

+ 0
- 7
crawler/log.go View File

@ -1,7 +0,0 @@
package crawler
import (
"github.com/tendermint/log15"
)
var log = log15.New("module", "crawler")

BIN
docs/sts-final.pdf View File


+ 6
- 14
mempool/mempool.go View File

@ -18,14 +18,12 @@ import (
type Mempool struct {
mtx sync.Mutex
state *sm.State
cache *sm.BlockCache
txs []types.Tx // TODO: we need to add a map to facilitate replace-by-fee
}
func NewMempool(state *sm.State) *Mempool {
return &Mempool{
state: state,
cache: sm.NewBlockCache(state),
}
}
@ -33,10 +31,6 @@ func (mem *Mempool) GetState() *sm.State {
return mem.state
}
func (mem *Mempool) GetCache() *sm.BlockCache {
return mem.cache
}
func (mem *Mempool) GetHeight() int {
mem.mtx.Lock()
defer mem.mtx.Unlock()
@ -47,7 +41,7 @@ func (mem *Mempool) GetHeight() int {
func (mem *Mempool) AddTx(tx types.Tx) (err error) {
mem.mtx.Lock()
defer mem.mtx.Unlock()
err = sm.ExecTx(mem.cache, tx, false, nil)
err = sm.ExecTx(mem.state, tx, nil)
if err != nil {
log.Info("AddTx() error", "tx", tx, "error", err)
return err
@ -85,12 +79,11 @@ func (mem *Mempool) ResetForBlockAndState(block *types.Block, state *sm.State) R
mem.mtx.Lock()
defer mem.mtx.Unlock()
mem.state = state.Copy()
mem.cache = sm.NewBlockCache(mem.state)
// First, create a lookup map of txns in new block.
blockTxsMap := make(map[string]struct{})
for _, tx := range block.Data.Txs {
blockTxsMap[string(types.TxID(state.ChainID, tx))] = struct{}{}
blockTxsMap[string(tx)] = struct{}{}
}
// Now we filter all txs from mem.txs that are in blockTxsMap,
@ -101,20 +94,19 @@ func (mem *Mempool) ResetForBlockAndState(block *types.Block, state *sm.State) R
var validTxs []types.Tx
includedStart, invalidStart := -1, -1
for i, tx := range mem.txs {
txID := types.TxID(state.ChainID, tx)
if _, ok := blockTxsMap[string(txID)]; ok {
if _, ok := blockTxsMap[string(tx)]; ok {
startRange(&includedStart, i) // start counting included txs
endRange(&invalidStart, i, &ri.Invalid) // stop counting invalid txs
log.Info("Filter out, already committed", "tx", tx, "txID", txID)
log.Info("Filter out, already committed", "tx", tx)
} else {
endRange(&includedStart, i, &ri.Included) // stop counting included txs
err := sm.ExecTx(mem.cache, tx, false, nil)
err := sm.ExecTx(mem.state, tx, nil)
if err != nil {
startRange(&invalidStart, i) // start counting invalid txs
log.Info("Filter out, no longer valid", "tx", tx, "error", err)
} else {
endRange(&invalidStart, i, &ri.Invalid) // stop counting invalid txs
log.Info("Filter in, new, valid", "tx", tx, "txID", txID)
log.Info("Filter in, new, valid", "tx", tx)
validTxs = append(validTxs, tx)
}
}


+ 0
- 273
mempool/mempool_test.go View File

@ -1,273 +0,0 @@
package mempool
import (
"fmt"
"sync"
"testing"
"time"
acm "github.com/tendermint/tendermint/account"
_ "github.com/tendermint/tendermint/config/tendermint_test"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
var someAddr = []byte("ABCDEFGHIJABCDEFGHIJ")
// number of txs
var nTxs = 100
// what the ResetInfo should look like after ResetForBlockAndState
var TestResetInfoData = ResetInfo{
Included: []Range{
Range{0, 5},
Range{10, 10},
Range{30, 5},
},
Invalid: []Range{
Range{5, 5},
Range{20, 8}, // let 28 and 29 be valid
Range{35, 64}, // let 99 be valid
},
}
// inverse of the ResetInfo
var notInvalidNotIncluded = map[int]struct{}{
28: struct{}{},
29: struct{}{},
99: struct{}{},
}
func newSendTx(t *testing.T, mempool *Mempool, from *acm.PrivAccount, to []byte, amt int64) types.Tx {
tx := types.NewSendTx()
tx.AddInput(mempool.GetCache(), from.PubKey, amt)
tx.AddOutput(to, amt)
tx.SignInput(config.GetString("chain_id"), 0, from)
if err := mempool.AddTx(tx); err != nil {
t.Fatal(err)
}
return tx
}
func addTxs(t *testing.T, mempool *Mempool, lastAcc *acm.PrivAccount, privAccs []*acm.PrivAccount) []types.Tx {
txs := make([]types.Tx, nTxs)
for i := 0; i < nTxs; i++ {
if _, ok := notInvalidNotIncluded[i]; ok {
txs[i] = newSendTx(t, mempool, lastAcc, someAddr, 10)
} else {
txs[i] = newSendTx(t, mempool, privAccs[i%len(privAccs)], privAccs[(i+1)%len(privAccs)].Address, 5)
}
}
return txs
}
func makeBlock(mempool *Mempool) *types.Block {
txs := mempool.GetProposalTxs()
var includedTxs []types.Tx
for _, rid := range TestResetInfoData.Included {
includedTxs = append(includedTxs, txs[rid.Start:rid.Start+rid.Length]...)
}
mempool.mtx.Lock()
state := mempool.state
state.LastBlockHeight += 1
mempool.mtx.Unlock()
return &types.Block{
Header: &types.Header{
ChainID: state.ChainID,
Height: state.LastBlockHeight,
NumTxs: len(includedTxs),
},
Data: &types.Data{
Txs: includedTxs,
},
}
}
// Add txs. Grab chunks to put in block. All the others become invalid because of nonce errors except those in notInvalidNotIncluded
func TestResetInfo(t *testing.T) {
amtPerAccount := int64(100000)
state, privAccs, _ := sm.RandGenesisState(6, false, amtPerAccount, 1, true, 100)
mempool := NewMempool(state)
lastAcc := privAccs[5] // we save him (his tx wont become invalid)
privAccs = privAccs[:5]
txs := addTxs(t, mempool, lastAcc, privAccs)
// its actually an invalid block since we're skipping nonces
// but all we care about is how the mempool responds after
block := makeBlock(mempool)
ri := mempool.ResetForBlockAndState(block, state)
if len(ri.Included) != len(TestResetInfoData.Included) {
t.Fatalf("invalid number of included ranges. Got %d, expected %d\n", len(ri.Included), len(TestResetInfoData.Included))
}
if len(ri.Invalid) != len(TestResetInfoData.Invalid) {
t.Fatalf("invalid number of invalid ranges. Got %d, expected %d\n", len(ri.Invalid), len(TestResetInfoData.Invalid))
}
for i, rid := range ri.Included {
inc := TestResetInfoData.Included[i]
if rid.Start != inc.Start {
t.Fatalf("Invalid start of range. Got %d, expected %d\n", inc.Start, rid.Start)
}
if rid.Length != inc.Length {
t.Fatalf("Invalid length of range. Got %d, expected %d\n", inc.Length, rid.Length)
}
}
txs = mempool.GetProposalTxs()
if len(txs) != len(notInvalidNotIncluded) {
t.Fatalf("Expected %d txs left in mempool. Got %d", len(notInvalidNotIncluded), len(txs))
}
}
//------------------------------------------------------------------------------------------
type TestPeer struct {
sync.Mutex
running bool
height int
t *testing.T
received int
txs map[string]int
timeoutFail int
done chan int
}
func newPeer(t *testing.T, state *sm.State) *TestPeer {
return &TestPeer{
running: true,
height: state.LastBlockHeight,
t: t,
txs: make(map[string]int),
done: make(chan int),
}
}
func (tp *TestPeer) IsRunning() bool {
tp.Lock()
defer tp.Unlock()
return tp.running
}
func (tp *TestPeer) SetRunning(running bool) {
tp.Lock()
defer tp.Unlock()
tp.running = running
}
func (tp *TestPeer) Send(chID byte, msg interface{}) bool {
if tp.timeoutFail > 0 {
time.Sleep(time.Second * time.Duration(tp.timeoutFail))
return false
}
tx := msg.(*TxMessage).Tx
id := types.TxID(config.GetString("chain_id"), tx)
if _, ok := tp.txs[string(id)]; ok {
tp.t.Fatal("received the same tx twice!")
}
tp.txs[string(id)] = tp.received
tp.received += 1
tp.done <- tp.received
return true
}
func (tp *TestPeer) Get(key string) interface{} {
return tp
}
func (tp *TestPeer) GetHeight() int {
return tp.height
}
func TestBroadcast(t *testing.T) {
state, privAccs, _ := sm.RandGenesisState(6, false, 10000, 1, true, 100)
mempool := NewMempool(state)
reactor := NewMempoolReactor(mempool)
reactor.Start()
lastAcc := privAccs[5] // we save him (his tx wont become invalid)
privAccs = privAccs[:5]
peer := newPeer(t, state)
newBlockChan := make(chan ResetInfo)
tickerChan := make(chan time.Time)
go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer)
// we don't broadcast any before updating
fmt.Println("dont broadcast any")
addTxs(t, mempool, lastAcc, privAccs)
block := makeBlock(mempool)
ri := mempool.ResetForBlockAndState(block, state)
newBlockChan <- ri
peer.height = ri.Height
tickerChan <- time.Now()
pullTxs(t, peer, len(mempool.txs)) // should have sent whatever txs are left (3)
toBroadcast := []int{1, 3, 7, 9, 11, 12, 18, 20, 21, 28, 29, 30, 31, 34, 35, 36, 50, 90, 99, 100}
for _, N := range toBroadcast {
peer = resetPeer(t, reactor, mempool, state, tickerChan, newBlockChan, peer)
// we broadcast N txs before updating
fmt.Println("broadcast", N)
addTxs(t, mempool, lastAcc, privAccs)
txsToSendPerCheck = N
tickerChan <- time.Now()
pullTxs(t, peer, txsToSendPerCheck) // should have sent N txs
block = makeBlock(mempool)
ri := mempool.ResetForBlockAndState(block, state)
newBlockChan <- ri
peer.height = ri.Height
txsToSendPerCheck = 100
tickerChan <- time.Now()
left := len(mempool.txs)
if N > 99 {
left -= 3
} else if N > 29 {
left -= 2
} else if N > 28 {
left -= 1
}
pullTxs(t, peer, left) // should have sent whatever txs are left that havent been sent
}
}
func pullTxs(t *testing.T, peer *TestPeer, N int) {
timer := time.NewTicker(time.Second * 2)
for i := 0; i < N; i++ {
select {
case <-peer.done:
case <-timer.C:
panic(fmt.Sprintf("invalid number of received messages. Got %d, expected %d\n", i, N))
}
}
if N == 0 {
select {
case <-peer.done:
t.Fatalf("should not have sent any more txs")
case <-timer.C:
}
}
}
func resetPeer(t *testing.T, reactor *MempoolReactor, mempool *Mempool, state *sm.State, tickerChan chan time.Time, newBlockChan chan ResetInfo, peer *TestPeer) *TestPeer {
// reset peer
mempool.txs = []types.Tx{}
mempool.state = state
mempool.cache = sm.NewBlockCache(state)
peer.SetRunning(false)
tickerChan <- time.Now()
peer = newPeer(t, state)
go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer)
return peer
}

+ 4
- 4
node/id.go View File

@ -1,18 +1,18 @@
package node
import (
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
"time"
)
type NodeID struct {
Name string
PubKey acm.PubKey
PubKey crypto.PubKey
}
type PrivNodeID struct {
NodeID
PrivKey acm.PrivKey
PrivKey crypto.PrivKey
}
type NodeGreeting struct {
@ -25,7 +25,7 @@ type NodeGreeting struct {
type SignedNodeGreeting struct {
NodeGreeting
Signature acm.Signature
Signature crypto.Signature
}
func (pnid *PrivNodeID) SignGreeting() *SignedNodeGreeting {


+ 26
- 32
node/node.go View File

@ -10,22 +10,20 @@ import (
"strings"
"time"
acm "github.com/tendermint/tendermint/account"
bc "github.com/tendermint/tendermint/blockchain"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/consensus"
"github.com/tendermint/go-crypto"
dbm "github.com/tendermint/go-db"
"github.com/tendermint/go-p2p"
"github.com/tendermint/go-wire"
bc "github.com/tendermint/tendermint/blockchain"
"github.com/tendermint/tendermint/consensus"
"github.com/tendermint/tendermint/events"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/go-p2p"
"github.com/tendermint/tendermint/rpc"
"github.com/tendermint/tendermint/rpc/core"
"github.com/tendermint/tendermint/rpc/server"
sm "github.com/tendermint/tendermint/state"
stypes "github.com/tendermint/tendermint/state/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/vm"
"github.com/tendermint/go-wire"
)
import _ "net/http/pprof"
@ -41,8 +39,8 @@ type Node struct {
consensusState *consensus.ConsensusState
consensusReactor *consensus.ConsensusReactor
privValidator *types.PrivValidator
genDoc *stypes.GenesisDoc
privKey acm.PrivKeyEd25519
genDoc *types.GenesisDoc
privKey crypto.PrivKeyEd25519
}
func NewNode() *Node {
@ -53,19 +51,19 @@ func NewNode() *Node {
// Get State
stateDB := dbm.GetDB("state")
state := sm.LoadState(stateDB)
var genDoc *stypes.GenesisDoc
var genDoc *types.GenesisDoc
if state == nil {
genDoc, state = sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file"))
state.Save()
// write the gendoc to db
buf, n, err := new(bytes.Buffer), new(int64), new(error)
wire.WriteJSON(genDoc, buf, n, err)
stateDB.Set(stypes.GenDocKey, buf.Bytes())
stateDB.Set(types.GenDocKey, buf.Bytes())
if *err != nil {
Exit(Fmt("Unable to write gendoc to db: %v", err))
}
} else {
genDocBytes := stateDB.Get(stypes.GenDocKey)
genDocBytes := stateDB.Get(types.GenDocKey)
err := new(error)
wire.ReadJSONPtr(&genDoc, genDocBytes, err)
if *err != nil {
@ -80,7 +78,7 @@ func NewNode() *Node {
privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
// Generate node PrivKey
privKey := acm.GenPrivKeyEd25519()
privKey := crypto.GenPrivKeyEd25519()
// Make event switch
eventSwitch := events.NewEventSwitch()
@ -116,7 +114,7 @@ func NewNode() *Node {
// add the event switch to all services
// they should all satisfy events.Eventable
SetFireable(eventSwitch, pexReactor, bcReactor, mempoolReactor, consensusReactor)
SetFireable(eventSwitch, bcReactor, mempoolReactor, consensusReactor)
// run the profile server
profileHost := config.GetString("prof_laddr")
@ -126,9 +124,6 @@ func NewNode() *Node {
}()
}
// set vm log level
vm.SetDebug(config.GetBool("vm_log"))
return &Node{
sw: sw,
evsw: eventSwitch,
@ -243,23 +238,23 @@ func (n *Node) EventSwitch() *events.EventSwitch {
return n.evsw
}
func makeNodeInfo(sw *p2p.Switch, privKey acm.PrivKeyEd25519) *types.NodeInfo {
func makeNodeInfo(sw *p2p.Switch, privKey crypto.PrivKeyEd25519) *p2p.NodeInfo {
nodeInfo := &types.NodeInfo{
PubKey: privKey.PubKey().(acm.PubKeyEd25519),
nodeInfo := &p2p.NodeInfo{
PubKey: privKey.PubKey().(crypto.PubKeyEd25519),
Moniker: config.GetString("moniker"),
ChainID: config.GetString("chain_id"),
Version: types.Versions{
Tendermint: Version,
P2P: p2p.Version,
RPC: rpc.Version,
Wire: wire.Version,
Network: config.GetString("chain_id"),
Version: Version,
Other: []string{
Fmt("p2p_version=%v" + p2p.Version),
Fmt("rpc_version=%v" + rpc.Version),
Fmt("wire_version=%v" + wire.Version),
},
}
// include git hash in the nodeInfo if available
if rev, err := ReadFile(config.GetString("revision_file")); err == nil {
nodeInfo.Version.Revision = string(rev)
nodeInfo.Other = append(nodeInfo.Other, Fmt("revision=%v", string(rev)))
}
if !sw.IsListening() {
@ -279,9 +274,8 @@ func makeNodeInfo(sw *p2p.Switch, privKey acm.PrivKeyEd25519) *types.NodeInfo {
// We assume that the rpcListener has the same ExternalAddress.
// This is probably true because both P2P and RPC listeners use UPnP,
// except of course if the rpc is only bound to localhost
nodeInfo.Host = p2pHost
nodeInfo.P2PPort = p2pPort
nodeInfo.RPCPort = uint16(rpcPort)
nodeInfo.Address = Fmt("%v:%v", p2pHost, p2pPort)
nodeInfo.Other = append(nodeInfo.Other, Fmt("rpc_port=%v", rpcPort))
return nodeInfo
}
@ -302,7 +296,7 @@ func RunNode() {
if err != nil {
Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
}
genDoc := stypes.GenesisDocFromJSON(jsonBlob)
genDoc := types.GenesisDocFromJSON(jsonBlob)
if genDoc.ChainID == "" {
PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile))
}
@ -313,7 +307,7 @@ func RunNode() {
// Create & start node
n := NewNode()
l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"))
l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp"))
n.AddListener(l)
err := n.Start()
if err != nil {


+ 2
- 2
node/node_test.go View File

@ -4,14 +4,14 @@ import (
"testing"
"time"
_ "github.com/tendermint/tendermint/config/tendermint_test"
"github.com/tendermint/go-p2p"
_ "github.com/tendermint/tendermint/config/tendermint_test"
)
func TestNodeStartStop(t *testing.T) {
// Create & start node
n := NewNode()
l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"))
l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp"))
n.AddListener(l)
n.Start()
log.Notice("Started node", "nodeInfo", n.sw.NodeInfo())


+ 0
- 23
permission/types/errors.go View File

@ -1,23 +0,0 @@
package types
import (
"fmt"
)
//------------------------------------------------------------------------------------------------
// Some errors
// permission number out of bounds
type ErrInvalidPermission PermFlag
func (e ErrInvalidPermission) Error() string {
return fmt.Sprintf("invalid permission %d", e)
}
// set=false. This error should be caught and the global
// value fetched for the permission by the caller
type ErrValueNotSet PermFlag
func (e ErrValueNotSet) Error() string {
return fmt.Sprintf("the value for permission %d is not set", e)
}

+ 0
- 238
permission/types/permissions.go View File

@ -1,238 +0,0 @@
package types
import (
"fmt"
. "github.com/tendermint/go-common"
)
//------------------------------------------------------------------------------------------------
var (
GlobalPermissionsAddress = Zero256[:20]
GlobalPermissionsAddress256 = Zero256
)
// A particular permission
type PermFlag uint64
// Base permission references are like unix (the index is already bit shifted)
const (
// chain permissions
Root PermFlag = 1 << iota // 1
Send // 2
Call // 4
CreateContract // 8
CreateAccount // 16
Bond // 32
Name // 64
// moderator permissions
HasBase
SetBase
UnsetBase
SetGlobal
HasRole
AddRole
RmRole
NumPermissions uint = 14 // NOTE Adjust this too. We can support upto 64
TopPermFlag PermFlag = 1 << (NumPermissions - 1)
AllPermFlags PermFlag = TopPermFlag | (TopPermFlag - 1)
DefaultPermFlags PermFlag = Send | Call | CreateContract | CreateAccount | Bond | Name | HasBase | HasRole
)
var (
ZeroBasePermissions = BasePermissions{0, 0}
ZeroAccountPermissions = AccountPermissions{
Base: ZeroBasePermissions,
}
DefaultAccountPermissions = AccountPermissions{
Base: BasePermissions{
Perms: DefaultPermFlags,
SetBit: AllPermFlags,
},
Roles: []string{},
}
)
//---------------------------------------------------------------------------------------------
// Base chain permissions struct
type BasePermissions struct {
// bit array with "has"/"doesn't have" for each permission
Perms PermFlag `json:"perms"`
// bit array with "set"/"not set" for each permission (not-set should fall back to global)
SetBit PermFlag `json:"set"`
}
// Get a permission value. ty should be a power of 2.
// ErrValueNotSet is returned if the permission's set bit is off,
// and should be caught by caller so the global permission can be fetched
func (p *BasePermissions) Get(ty PermFlag) (bool, error) {
if ty == 0 {
return false, ErrInvalidPermission(ty)
}
if p.SetBit&ty == 0 {
return false, ErrValueNotSet(ty)
}
return p.Perms&ty > 0, nil
}
// Set a permission bit. Will set the permission's set bit to true.
func (p *BasePermissions) Set(ty PermFlag, value bool) error {
if ty == 0 {
return ErrInvalidPermission(ty)
}
p.SetBit |= ty
if value {
p.Perms |= ty
} else {
p.Perms &= ^ty
}
return nil
}
// Set the permission's set bit to false
func (p *BasePermissions) Unset(ty PermFlag) error {
if ty == 0 {
return ErrInvalidPermission(ty)
}
p.SetBit &= ^ty
return nil
}
// Check if the permission is set
func (p *BasePermissions) IsSet(ty PermFlag) bool {
if ty == 0 {
return false
}
return p.SetBit&ty > 0
}
func (p BasePermissions) String() string {
return fmt.Sprintf("Base: %b; Set: %b", p.Perms, p.SetBit)
}
//---------------------------------------------------------------------------------------------
type AccountPermissions struct {
Base BasePermissions `json:"base"`
Roles []string `json:"roles"`
}
// Returns true if the role is found
func (aP *AccountPermissions) HasRole(role string) bool {
role = string(LeftPadBytes([]byte(role), 32))
for _, r := range aP.Roles {
if r == role {
return true
}
}
return false
}
// Returns true if the role is added, and false if it already exists
func (aP *AccountPermissions) AddRole(role string) bool {
role = string(LeftPadBytes([]byte(role), 32))
for _, r := range aP.Roles {
if r == role {
return false
}
}
aP.Roles = append(aP.Roles, role)
return true
}
// Returns true if the role is removed, and false if it is not found
func (aP *AccountPermissions) RmRole(role string) bool {
role = string(LeftPadBytes([]byte(role), 32))
for i, r := range aP.Roles {
if r == role {
post := []string{}
if len(aP.Roles) > i+1 {
post = aP.Roles[i+1:]
}
aP.Roles = append(aP.Roles[:i], post...)
return true
}
}
return false
}
//--------------------------------------------------------------------------------
// string utilities
// PermFlagToString assumes the permFlag is valid, else returns "#-UNKNOWN-#"
func PermFlagToString(pf PermFlag) (perm string) {
switch pf {
case Root:
perm = "root"
case Send:
perm = "send"
case Call:
perm = "call"
case CreateContract:
perm = "create_contract"
case CreateAccount:
perm = "create_account"
case Bond:
perm = "bond"
case Name:
perm = "name"
case HasBase:
perm = "has_base"
case SetBase:
perm = "set_base"
case UnsetBase:
perm = "unset_base"
case SetGlobal:
perm = "set_global"
case HasRole:
perm = "has_role"
case AddRole:
perm = "add_role"
case RmRole:
perm = "rm_role"
default:
perm = "#-UNKNOWN-#"
}
return
}
func PermStringToFlag(perm string) (pf PermFlag, err error) {
switch perm {
case "root":
pf = Root
case "send":
pf = Send
case "call":
pf = Call
case "create_contract":
pf = CreateContract
case "create_account":
pf = CreateAccount
case "bond":
pf = Bond
case "name":
pf = Name
case "has_base":
pf = HasBase
case "set_base":
pf = SetBase
case "unset_base":
pf = UnsetBase
case "set_global":
pf = SetGlobal
case "has_role":
pf = HasRole
case "add_role":
pf = AddRole
case "rm_role":
pf = RmRole
default:
err = fmt.Errorf("Unknown permission %s", perm)
}
return
}

+ 0
- 102
permission/types/snatives.go View File

@ -1,102 +0,0 @@
package types
import (
"github.com/tendermint/go-wire"
)
//---------------------------------------------------------------------------------------------------
// PermissionsTx.PermArgs interface and argument encoding
// Arguments are a registered interface in the PermissionsTx,
// so binary handles the arguments and each permission function gets a type-byte
// PermFlag() maps the type-byte to the permission
// The account sending the PermissionsTx must have this PermFlag set
type PermArgs interface {
PermFlag() PermFlag
}
const (
PermArgsTypeHasBase = byte(0x01)
PermArgsTypeSetBase = byte(0x02)
PermArgsTypeUnsetBase = byte(0x03)
PermArgsTypeSetGlobal = byte(0x04)
PermArgsTypeHasRole = byte(0x05)
PermArgsTypeAddRole = byte(0x06)
PermArgsTypeRmRole = byte(0x07)
)
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ PermArgs }{},
wire.ConcreteType{&HasBaseArgs{}, PermArgsTypeHasBase},
wire.ConcreteType{&SetBaseArgs{}, PermArgsTypeSetBase},
wire.ConcreteType{&UnsetBaseArgs{}, PermArgsTypeUnsetBase},
wire.ConcreteType{&SetGlobalArgs{}, PermArgsTypeSetGlobal},
wire.ConcreteType{&HasRoleArgs{}, PermArgsTypeHasRole},
wire.ConcreteType{&AddRoleArgs{}, PermArgsTypeAddRole},
wire.ConcreteType{&RmRoleArgs{}, PermArgsTypeRmRole},
)
type HasBaseArgs struct {
Address []byte `json:"address"`
Permission PermFlag `json:"permission"`
}
func (*HasBaseArgs) PermFlag() PermFlag {
return HasBase
}
type SetBaseArgs struct {
Address []byte `json:"address"`
Permission PermFlag `json:"permission"`
Value bool `json:"value"`
}
func (*SetBaseArgs) PermFlag() PermFlag {
return SetBase
}
type UnsetBaseArgs struct {
Address []byte `json:"address"`
Permission PermFlag `json:"permission"`
}
func (*UnsetBaseArgs) PermFlag() PermFlag {
return UnsetBase
}
type SetGlobalArgs struct {
Permission PermFlag `json:"permission"`
Value bool `json:"value"`
}
func (*SetGlobalArgs) PermFlag() PermFlag {
return SetGlobal
}
type HasRoleArgs struct {
Address []byte `json:"address"`
Role string `json:"role"`
}
func (*HasRoleArgs) PermFlag() PermFlag {
return HasRole
}
type AddRoleArgs struct {
Address []byte `json:"address"`
Role string `json:"role"`
}
func (*AddRoleArgs) PermFlag() PermFlag {
return AddRole
}
type RmRoleArgs struct {
Address []byte `json:"address"`
Role string `json:"role"`
}
func (*RmRoleArgs) PermFlag() PermFlag {
return RmRole
}

+ 1
- 1
rpc/client/client.go View File

@ -8,8 +8,8 @@ import (
"net/http"
. "github.com/tendermint/go-common"
. "github.com/tendermint/tendermint/rpc/types"
"github.com/tendermint/go-wire"
. "github.com/tendermint/tendermint/rpc/types"
)
func Call(remote string, method string, params []interface{}, dest interface{}) (interface{}, error) {


+ 0
- 67
rpc/core/accounts.go View File

@ -1,67 +0,0 @@
package core
import (
"fmt"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
func GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) {
return &ctypes.ResultGenPrivAccount{acm.GenPrivAccount()}, nil
}
// If the account is not known, returns nil, nil.
func GetAccount(address []byte) (*ctypes.ResultGetAccount, error) {
cache := mempoolReactor.Mempool.GetCache()
account := cache.GetAccount(address)
if account == nil {
return nil, nil
}
return &ctypes.ResultGetAccount{account}, nil
}
func GetStorage(address, key []byte) (*ctypes.ResultGetStorage, error) {
state := consensusState.GetState()
account := state.GetAccount(address)
if account == nil {
return nil, fmt.Errorf("UnknownAddress: %X", address)
}
storageRoot := account.StorageRoot
storageTree := state.LoadStorage(storageRoot)
_, value := storageTree.Get(LeftPadWord256(key).Bytes())
if value == nil {
return &ctypes.ResultGetStorage{key, nil}, nil
}
return &ctypes.ResultGetStorage{key, value.([]byte)}, nil
}
func ListAccounts() (*ctypes.ResultListAccounts, error) {
var blockHeight int
var accounts []*acm.Account
state := consensusState.GetState()
blockHeight = state.LastBlockHeight
state.GetAccounts().Iterate(func(key interface{}, value interface{}) bool {
accounts = append(accounts, value.(*acm.Account))
return false
})
return &ctypes.ResultListAccounts{blockHeight, accounts}, nil
}
func DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) {
state := consensusState.GetState()
account := state.GetAccount(address)
if account == nil {
return nil, fmt.Errorf("UnknownAddress: %X", address)
}
storageRoot := account.StorageRoot
storageTree := state.LoadStorage(storageRoot)
storageItems := []ctypes.StorageItem{}
storageTree.Iterate(func(key interface{}, value interface{}) bool {
storageItems = append(storageItems, ctypes.StorageItem{
key.([]byte), value.([]byte)})
return false
})
return &ctypes.ResultDumpStorage{storageRoot, storageItems}, nil
}

+ 5
- 10
rpc/core/consensus.go View File

@ -1,29 +1,24 @@
package core
import (
"github.com/tendermint/go-wire"
cm "github.com/tendermint/tendermint/consensus"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/go-wire"
)
func ListValidators() (*ctypes.ResultListValidators, error) {
var blockHeight int
var bondedValidators []*types.Validator
var unbondingValidators []*types.Validator
var validators []*types.Validator
state := consensusState.GetState()
blockHeight = state.LastBlockHeight
state.BondedValidators.Iterate(func(index int, val *types.Validator) bool {
bondedValidators = append(bondedValidators, val)
return false
})
state.UnbondingValidators.Iterate(func(index int, val *types.Validator) bool {
unbondingValidators = append(unbondingValidators, val)
state.Validators.Iterate(func(index int, val *types.Validator) bool {
validators = append(validators, val)
return false
})
return &ctypes.ResultListValidators{blockHeight, bondedValidators, unbondingValidators}, nil
return &ctypes.ResultListValidators{blockHeight, validators}, nil
}
func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {


+ 1
- 13
rpc/core/mempool.go View File

@ -3,7 +3,6 @@ package core
import (
"fmt"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@ -15,18 +14,7 @@ func BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
if err != nil {
return nil, fmt.Errorf("Error broadcasting transaction: %v", err)
}
txHash := types.TxID(mempoolReactor.Mempool.GetState().ChainID, tx)
var createsContract uint8
var contractAddr []byte
// check if creates new contract
if callTx, ok := tx.(*types.CallTx); ok {
if len(callTx.Address) == 0 {
createsContract = 1
contractAddr = state.NewContractAddress(callTx.Input.Address, callTx.Input.Sequence)
}
}
return &ctypes.ResultBroadcastTx{ctypes.Receipt{txHash, createsContract, contractAddr}}, nil
return &ctypes.ResultBroadcastTx{}, nil
}
func ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) {


+ 0
- 29
rpc/core/names.go View File

@ -1,29 +0,0 @@
package core
import (
"fmt"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
)
func GetName(name string) (*ctypes.ResultGetName, error) {
st := consensusState.GetState() // performs a copy
entry := st.GetNameRegEntry(name)
if entry == nil {
return nil, fmt.Errorf("Name %s not found", name)
}
return &ctypes.ResultGetName{entry}, nil
}
func ListNames() (*ctypes.ResultListNames, error) {
var blockHeight int
var names []*types.NameRegEntry
state := consensusState.GetState()
blockHeight = state.LastBlockHeight
state.GetNames().Iterate(func(key interface{}, value interface{}) bool {
names = append(names, value.(*types.NameRegEntry))
return false
})
return &ctypes.ResultListNames{blockHeight, names}, nil
}

+ 3
- 4
rpc/core/pipe.go View File

@ -1,11 +1,10 @@
package core
import (
"github.com/tendermint/go-p2p"
bc "github.com/tendermint/tendermint/blockchain"
"github.com/tendermint/tendermint/consensus"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/go-p2p"
stypes "github.com/tendermint/tendermint/state/types"
"github.com/tendermint/tendermint/types"
)
@ -15,7 +14,7 @@ var consensusReactor *consensus.ConsensusReactor
var mempoolReactor *mempl.MempoolReactor
var p2pSwitch *p2p.Switch
var privValidator *types.PrivValidator
var genDoc *stypes.GenesisDoc // cache the genesis structure
var genDoc *types.GenesisDoc // cache the genesis structure
func SetBlockStore(bs *bc.BlockStore) {
blockStore = bs
@ -41,6 +40,6 @@ func SetPrivValidator(pv *types.PrivValidator) {
privValidator = pv
}
func SetGenDoc(doc *stypes.GenesisDoc) {
func SetGenDoc(doc *types.GenesisDoc) {
genDoc = doc
}

+ 9
- 19
rpc/core/routes.go View File

@ -6,24 +6,14 @@ import (
// TODO: eliminate redundancy between here and reading code from core/
var Routes = map[string]*rpc.RPCFunc{
"status": rpc.NewRPCFunc(Status, []string{}),
"net_info": rpc.NewRPCFunc(NetInfo, []string{}),
"blockchain": rpc.NewRPCFunc(BlockchainInfo, []string{"minHeight", "maxHeight"}),
"genesis": rpc.NewRPCFunc(Genesis, []string{}),
"get_block": rpc.NewRPCFunc(GetBlock, []string{"height"}),
"get_account": rpc.NewRPCFunc(GetAccount, []string{"address"}),
"get_storage": rpc.NewRPCFunc(GetStorage, []string{"address", "key"}),
"call": rpc.NewRPCFunc(Call, []string{"fromAddress", "toAddress", "data"}),
"call_code": rpc.NewRPCFunc(CallCode, []string{"fromAddress", "code", "data"}),
"list_validators": rpc.NewRPCFunc(ListValidators, []string{}),
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, []string{}),
"dump_storage": rpc.NewRPCFunc(DumpStorage, []string{"address"}),
"broadcast_tx": rpc.NewRPCFunc(BroadcastTx, []string{"tx"}),
"list_unconfirmed_txs": rpc.NewRPCFunc(ListUnconfirmedTxs, []string{}),
"list_accounts": rpc.NewRPCFunc(ListAccounts, []string{}),
"get_name": rpc.NewRPCFunc(GetName, []string{"name"}),
"list_names": rpc.NewRPCFunc(ListNames, []string{}),
"unsafe/gen_priv_account": rpc.NewRPCFunc(GenPrivAccount, []string{}),
"unsafe/sign_tx": rpc.NewRPCFunc(SignTx, []string{"tx", "privAccounts"}),
"status": rpc.NewRPCFunc(Status, []string{}),
"net_info": rpc.NewRPCFunc(NetInfo, []string{}),
"blockchain": rpc.NewRPCFunc(BlockchainInfo, []string{"minHeight", "maxHeight"}),
"genesis": rpc.NewRPCFunc(Genesis, []string{}),
"get_block": rpc.NewRPCFunc(GetBlock, []string{"height"}),
"list_validators": rpc.NewRPCFunc(ListValidators, []string{}),
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, []string{}),
"broadcast_tx": rpc.NewRPCFunc(BroadcastTx, []string{"tx"}),
"list_unconfirmed_txs": rpc.NewRPCFunc(ListUnconfirmedTxs, []string{}),
// subscribe/unsubscribe are reserved for websocket events.
}

+ 0
- 116
rpc/core/txs.go View File

@ -1,116 +0,0 @@
package core
import (
"fmt"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/vm"
)
func toVMAccount(acc *acm.Account) *vm.Account {
return &vm.Account{
Address: LeftPadWord256(acc.Address),
Balance: acc.Balance,
Code: acc.Code, // This is crazy.
Nonce: int64(acc.Sequence),
Other: acc.PubKey,
}
}
//-----------------------------------------------------------------------------
// Run a contract's code on an isolated and unpersisted state
// Cannot be used to create new contracts
func Call(fromAddress, toAddress, data []byte) (*ctypes.ResultCall, error) {
st := consensusState.GetState() // performs a copy
cache := state.NewBlockCache(st)
outAcc := cache.GetAccount(toAddress)
if outAcc == nil {
return nil, fmt.Errorf("Account %x does not exist", toAddress)
}
callee := toVMAccount(outAcc)
caller := &vm.Account{Address: LeftPadWord256(fromAddress)}
txCache := state.NewTxCache(cache)
params := vm.Params{
BlockHeight: int64(st.LastBlockHeight),
BlockHash: LeftPadWord256(st.LastBlockHash),
BlockTime: st.LastBlockTime.Unix(),
GasLimit: st.GetGasLimit(),
}
vmach := vm.NewVM(txCache, params, caller.Address, nil)
gas := st.GetGasLimit()
ret, err := vmach.Call(caller, callee, callee.Code, data, 0, &gas)
if err != nil {
return nil, err
}
return &ctypes.ResultCall{Return: ret}, nil
}
// Run the given code on an isolated and unpersisted state
// Cannot be used to create new contracts
func CallCode(fromAddress, code, data []byte) (*ctypes.ResultCall, error) {
st := consensusState.GetState() // performs a copy
cache := mempoolReactor.Mempool.GetCache()
callee := &vm.Account{Address: LeftPadWord256(fromAddress)}
caller := &vm.Account{Address: LeftPadWord256(fromAddress)}
txCache := state.NewTxCache(cache)
params := vm.Params{
BlockHeight: int64(st.LastBlockHeight),
BlockHash: LeftPadWord256(st.LastBlockHash),
BlockTime: st.LastBlockTime.Unix(),
GasLimit: st.GetGasLimit(),
}
vmach := vm.NewVM(txCache, params, caller.Address, nil)
gas := st.GetGasLimit()
ret, err := vmach.Call(caller, callee, code, data, 0, &gas)
if err != nil {
return nil, err
}
return &ctypes.ResultCall{Return: ret}, nil
}
//-----------------------------------------------------------------------------
func SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) {
// more checks?
for i, privAccount := range privAccounts {
if privAccount == nil || privAccount.PrivKey == nil {
return nil, fmt.Errorf("Invalid (empty) privAccount @%v", i)
}
}
switch tx.(type) {
case *types.SendTx:
sendTx := tx.(*types.SendTx)
for i, input := range sendTx.Inputs {
input.PubKey = privAccounts[i].PubKey
input.Signature = privAccounts[i].Sign(config.GetString("chain_id"), sendTx)
}
case *types.CallTx:
callTx := tx.(*types.CallTx)
callTx.Input.PubKey = privAccounts[0].PubKey
callTx.Input.Signature = privAccounts[0].Sign(config.GetString("chain_id"), callTx)
case *types.BondTx:
bondTx := tx.(*types.BondTx)
// the first privaccount corresponds to the BondTx pub key.
// the rest to the inputs
bondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), bondTx).(acm.SignatureEd25519)
for i, input := range bondTx.Inputs {
input.PubKey = privAccounts[i+1].PubKey
input.Signature = privAccounts[i+1].Sign(config.GetString("chain_id"), bondTx)
}
case *types.UnbondTx:
unbondTx := tx.(*types.UnbondTx)
unbondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), unbondTx).(acm.SignatureEd25519)
case *types.RebondTx:
rebondTx := tx.(*types.RebondTx)
rebondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), rebondTx).(acm.SignatureEd25519)
}
return &ctypes.ResultSignTx{tx}, nil
}

+ 14
- 87
rpc/core/types/responses.go View File

@ -1,38 +1,12 @@
package core_types
import (
acm "github.com/tendermint/tendermint/account"
stypes "github.com/tendermint/tendermint/state/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-p2p"
"github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
)
type ResultGetStorage struct {
Key []byte `json:"key"`
Value []byte `json:"value"`
}
type ResultCall struct {
Return []byte `json:"return"`
GasUsed int64 `json:"gas_used"`
// TODO ...
}
type ResultListAccounts struct {
BlockHeight int `json:"block_height"`
Accounts []*acm.Account `json:"accounts"`
}
type ResultDumpStorage struct {
StorageRoot []byte `json:"storage_root"`
StorageItems []StorageItem `json:"storage_items"`
}
type StorageItem struct {
Key []byte `json:"key"`
Value []byte `json:"value"`
}
type ResultBlockchainInfo struct {
LastHeight int `json:"last_height"`
BlockMetas []*types.BlockMeta `json:"block_metas"`
@ -44,12 +18,12 @@ type ResultGetBlock struct {
}
type ResultStatus struct {
NodeInfo *types.NodeInfo `json:"node_info"`
GenesisHash []byte `json:"genesis_hash"`
PubKey acm.PubKey `json:"pub_key"`
LatestBlockHash []byte `json:"latest_block_hash"`
LatestBlockHeight int `json:"latest_block_height"`
LatestBlockTime int64 `json:"latest_block_time"` // nano
NodeInfo *p2p.NodeInfo `json:"node_info"`
GenesisHash []byte `json:"genesis_hash"`
PubKey crypto.PubKey `json:"pub_key"`
LatestBlockHash []byte `json:"latest_block_hash"`
LatestBlockHeight int `json:"latest_block_height"`
LatestBlockTime int64 `json:"latest_block_time"` // nano
}
type ResultNetInfo struct {
@ -59,14 +33,13 @@ type ResultNetInfo struct {
}
type Peer struct {
types.NodeInfo `json:"node_info"`
IsOutbound bool `json:"is_outbound"`
p2p.NodeInfo `json:"node_info"`
IsOutbound bool `json:"is_outbound"`
}
type ResultListValidators struct {
BlockHeight int `json:"block_height"`
BondedValidators []*types.Validator `json:"bonded_validators"`
UnbondingValidators []*types.Validator `json:"unbonding_validators"`
BlockHeight int `json:"block_height"`
Validators []*types.Validator `json:"validators"`
}
type ResultDumpConsensusState struct {
@ -74,27 +47,7 @@ type ResultDumpConsensusState struct {
PeerRoundStates []string `json:"peer_round_states"`
}
type ResultListNames struct {
BlockHeight int `json:"block_height"`
Names []*types.NameRegEntry `json:"names"`
}
type ResultGenPrivAccount struct {
PrivAccount *acm.PrivAccount `json:"priv_account"`
}
type ResultGetAccount struct {
Account *acm.Account `json:"account"`
}
type ResultBroadcastTx struct {
Receipt Receipt `json:"receipt"`
}
type Receipt struct {
TxHash []byte `json:"tx_hash"`
CreatesContract uint8 `json:"creates_contract"`
ContractAddr []byte `json:"contract_addr"`
}
type ResultListUnconfirmedTxs struct {
@ -102,16 +55,8 @@ type ResultListUnconfirmedTxs struct {
Txs []types.Tx `json:"txs"`
}
type ResultGetName struct {
Entry *types.NameRegEntry `json:"entry"`
}
type ResultGenesis struct {
Genesis *stypes.GenesisDoc `json:"genesis"`
}
type ResultSignTx struct {
Tx types.Tx `json:"tx"`
Genesis *types.GenesisDoc `json:"genesis"`
}
type ResultEvent struct {
@ -130,24 +75,15 @@ type Response struct {
}
const (
ResultTypeGetStorage = byte(0x01)
ResultTypeCall = byte(0x02)
ResultTypeListAccounts = byte(0x03)
ResultTypeDumpStorage = byte(0x04)
ResultTypeBlockchainInfo = byte(0x05)
ResultTypeGetBlock = byte(0x06)
ResultTypeStatus = byte(0x07)
ResultTypeNetInfo = byte(0x08)
ResultTypeListValidators = byte(0x09)
ResultTypeDumpConsensusState = byte(0x0A)
ResultTypeListNames = byte(0x0B)
ResultTypeGenPrivAccount = byte(0x0C)
ResultTypeGetAccount = byte(0x0D)
ResultTypeBroadcastTx = byte(0x0E)
ResultTypeListUnconfirmedTxs = byte(0x0F)
ResultTypeGetName = byte(0x10)
ResultTypeGenesis = byte(0x11)
ResultTypeSignTx = byte(0x12)
ResultTypeEvent = byte(0x13) // so websockets can respond to rpc functions
)
@ -156,23 +92,14 @@ type Result interface{}
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ Result }{},
wire.ConcreteType{&ResultGetStorage{}, ResultTypeGetStorage},
wire.ConcreteType{&ResultCall{}, ResultTypeCall},
wire.ConcreteType{&ResultListAccounts{}, ResultTypeListAccounts},
wire.ConcreteType{&ResultDumpStorage{}, ResultTypeDumpStorage},
wire.ConcreteType{&ResultBlockchainInfo{}, ResultTypeBlockchainInfo},
wire.ConcreteType{&ResultGetBlock{}, ResultTypeGetBlock},
wire.ConcreteType{&ResultStatus{}, ResultTypeStatus},
wire.ConcreteType{&ResultNetInfo{}, ResultTypeNetInfo},
wire.ConcreteType{&ResultListValidators{}, ResultTypeListValidators},
wire.ConcreteType{&ResultDumpConsensusState{}, ResultTypeDumpConsensusState},
wire.ConcreteType{&ResultListNames{}, ResultTypeListNames},
wire.ConcreteType{&ResultGenPrivAccount{}, ResultTypeGenPrivAccount},
wire.ConcreteType{&ResultGetAccount{}, ResultTypeGetAccount},
wire.ConcreteType{&ResultBroadcastTx{}, ResultTypeBroadcastTx},
wire.ConcreteType{&ResultListUnconfirmedTxs{}, ResultTypeListUnconfirmedTxs},
wire.ConcreteType{&ResultGetName{}, ResultTypeGetName},
wire.ConcreteType{&ResultGenesis{}, ResultTypeGenesis},
wire.ConcreteType{&ResultSignTx{}, ResultTypeSignTx},
wire.ConcreteType{&ResultEvent{}, ResultTypeEvent},
)

+ 0
- 236
rpc/core_client/client.go View File

@ -1,236 +0,0 @@
package core_client
import (
"bytes"
"fmt"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/types"
"github.com/tendermint/go-wire"
"io/ioutil"
"net/http"
"net/url"
//"reflect"
// Uncomment to use go:generate
// _ "github.com/tendermint/go-rpc-gen"
)
// maps camel-case function names to lower case rpc version
var reverseFuncMap = map[string]string{
"Status": "status",
"NetInfo": "net_info",
"BlockchainInfo": "blockchain",
"Genesis": "genesis",
"GetBlock": "get_block",
"GetAccount": "get_account",
"GetStorage": "get_storage",
"Call": "call",
"CallCode": "call_code",
"ListValidators": "list_validators",
"DumpConsensusState": "dump_consensus_state",
"DumpStorage": "dump_storage",
"BroadcastTx": "broadcast_tx",
"ListUnconfirmedTxs": "list_unconfirmed_txs",
"ListAccounts": "list_accounts",
"GetName": "get_name",
"ListNames": "list_names",
"GenPrivAccount": "unsafe/gen_priv_account",
"SignTx": "unsafe/sign_tx",
}
/*
// fill the map from camelcase to lowercase
func fillReverseFuncMap() map[string]string {
fMap := make(map[string]string)
for name, f := range core.Routes {
camelName := runtime.FuncForPC(f.f.Pointer()).Name()
spl := strings.Split(camelName, ".")
if len(spl) > 1 {
camelName = spl[len(spl)-1]
}
fMap[camelName] = name
}
return fMap
}
*/
type Response struct {
Status string
Data interface{}
Error string
}
//go:generate go-rpc-gen -interface Client -dir ../core -pkg core -type *ClientHTTP,*ClientJSON -exclude pipe.go -out-pkg core_client
type ClientJSON struct {
addr string
}
type ClientHTTP struct {
addr string
}
func NewClient(addr, typ string) Client {
switch typ {
case "HTTP":
return &ClientHTTP{addr}
case "JSONRPC":
return &ClientJSON{addr}
default:
panic("Unknown client type " + typ + ". Select HTTP or JSONRPC")
}
return nil
}
func argsToJson(args ...interface{}) ([]string, error) {
l := len(args)
jsons := make([]string, l)
n, err := new(int64), new(error)
for i, a := range args {
buf := new(bytes.Buffer)
wire.WriteJSON(a, buf, n, err)
if *err != nil {
return nil, *err
}
jsons[i] = string(buf.Bytes())
}
return jsons, nil
}
func (c *ClientJSON) RequestResponse(s rpctypes.RPCRequest) (b []byte, err error) {
b = wire.JSONBytes(s)
buf := bytes.NewBuffer(b)
resp, err := http.Post(c.addr, "text/json", buf)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
/*
What follows is used by `rpc-gen` when `go generate` is called
to populate the rpc client methods
*/
// first we define the base interface, which rpc-gen will further populate with generated methods
/*rpc-gen:define-interface Client
type Client interface {
Address() string // returns the remote address
}
*/
// encoding functions
func binaryWriter(args ...interface{}) ([]interface{}, error) {
list := []interface{}{}
for _, a := range args {
buf, n, err := new(bytes.Buffer), new(int64), new(error)
wire.WriteJSON(a, buf, n, err)
if *err != nil {
return nil, *err
}
list = append(list, buf.Bytes())
}
return list, nil
}
func argsToURLValues(argNames []string, args ...interface{}) (url.Values, error) {
values := make(url.Values)
if len(argNames) == 0 {
return values, nil
}
if len(argNames) != len(args) {
return nil, fmt.Errorf("argNames and args have different lengths: %d, %d", len(argNames), len(args))
}
slice, err := argsToJson(args...)
if err != nil {
return nil, err
}
for i, name := range argNames {
s := slice[i]
values.Set(name, s) // s[0]
/*for j := 1; j < len(s); j++ {
values.Add(name, s[j])
}*/
}
return values, nil
}
func unmarshalCheckResponse(body []byte) (response *ctypes.Response, err error) {
response = new(ctypes.Response)
wire.ReadJSON(response, body, &err)
if err != nil {
return nil, err
}
if response.Error != "" {
return nil, fmt.Errorf(response.Error)
}
return response, nil
}
// import statements we will need for the templates
/*rpc-gen:imports:
rpctypes github.com/tendermint/tendermint/rpc/types
net/http
io/ioutil
fmt
*/
// Template functions to be filled in
/*rpc-gen:template:*ClientJSON func (c *ClientJSON) {{name}}({{args.def}}) ({{response}}) {
request := rpctypes.RPCRequest{
JSONRPC: "2.0",
Method: reverseFuncMap["{{name}}"],
Params: []interface{}{ {{args.ident}} },
ID: "",
}
body, err := c.RequestResponse(request)
if err != nil{
return nil, err
}
response, err := unmarshalCheckResponse(body)
if err != nil{
return nil, err
}
if response.Result == nil {
return nil, nil
}
result, ok := response.Result.({{response.0}})
if !ok{
return nil, fmt.Errorf("response result was wrong type")
}
return result, nil
}*/
/*rpc-gen:template:*ClientHTTP func (c *ClientHTTP) {{name}}({{args.def}}) ({{response}}){
values, err := argsToURLValues({{args.name}}, {{args.ident}})
if err != nil{
return nil, err
}
resp, err := http.PostForm(c.addr+reverseFuncMap["{{name}}"], values)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
response, err := unmarshalCheckResponse(body)
if err != nil{
return nil, err
}
if response.Result == nil {
return nil, nil
}
result, ok := response.Result.({{response.0}})
if !ok{
return nil, fmt.Errorf("response result was wrong type")
}
return result, nil
}*/

+ 0
- 1042
rpc/core_client/client_methods.go
File diff suppressed because it is too large
View File


+ 0
- 8
rpc/core_client/log.go View File

@ -1,8 +0,0 @@
package core_client
import (
"github.com/tendermint/log15"
)
var log = log15.New("module", "core_client")

+ 0
- 119
rpc/core_client/ws_client.go View File

@ -1,119 +0,0 @@
package core_client
import (
"net/http"
"strings"
"time"
"github.com/gorilla/websocket"
. "github.com/tendermint/go-common"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/rpc/types"
"github.com/tendermint/go-wire"
)
const (
wsEventsChannelCapacity = 10
wsResultsChannelCapacity = 10
wsWriteTimeoutSeconds = 10
)
type WSClient struct {
QuitService
Address string
*websocket.Conn
EventsCh chan ctypes.ResultEvent
ResultsCh chan ctypes.Result
}
// create a new connection
func NewWSClient(addr string) *WSClient {
wsClient := &WSClient{
Address: addr,
Conn: nil,
EventsCh: make(chan ctypes.ResultEvent, wsEventsChannelCapacity),
ResultsCh: make(chan ctypes.Result, wsResultsChannelCapacity),
}
wsClient.QuitService = *NewQuitService(log, "WSClient", wsClient)
return wsClient
}
func (wsc *WSClient) OnStart() error {
wsc.QuitService.OnStart()
err := wsc.dial()
if err != nil {
return err
}
go wsc.receiveEventsRoutine()
return nil
}
func (wsc *WSClient) dial() error {
// Dial
dialer := websocket.DefaultDialer
rHeader := http.Header{}
con, _, err := dialer.Dial(wsc.Address, rHeader)
if err != nil {
return err
}
// Set the ping/pong handlers
con.SetPingHandler(func(m string) error {
con.WriteControl(websocket.PongMessage, []byte(m), time.Now().Add(time.Second*wsWriteTimeoutSeconds))
return nil
})
con.SetPongHandler(func(m string) error {
return nil
})
wsc.Conn = con
return nil
}
func (wsc *WSClient) OnStop() {
wsc.QuitService.OnStop()
}
func (wsc *WSClient) receiveEventsRoutine() {
for {
_, data, err := wsc.ReadMessage()
if err != nil {
log.Info("WSClient failed to read message", "error", err, "data", string(data))
wsc.Stop()
break
} else {
var response ctypes.Response
wire.ReadJSON(&response, data, &err)
if err != nil {
log.Info("WSClient failed to parse message", "error", err)
wsc.Stop()
break
}
if strings.HasSuffix(response.ID, "#event") {
wsc.EventsCh <- *response.Result.(*ctypes.ResultEvent)
} else {
wsc.ResultsCh <- response.Result
}
}
}
}
// subscribe to an event
func (wsc *WSClient) Subscribe(eventid string) error {
err := wsc.WriteJSON(rpctypes.RPCRequest{
JSONRPC: "2.0",
ID: "",
Method: "subscribe",
Params: []interface{}{eventid},
})
return err
}
// unsubscribe from an event
func (wsc *WSClient) Unsubscribe(eventid string) error {
err := wsc.WriteJSON(rpctypes.RPCRequest{
JSONRPC: "2.0",
ID: "",
Method: "unsubscribe",
Params: []interface{}{eventid},
})
return err
}

+ 0
- 129
rpc/test/client_rpc_test.go View File

@ -1,129 +0,0 @@
package rpctest
import (
_ "github.com/tendermint/tendermint/config/tendermint_test"
"testing"
)
// When run with `-test.short` we only run:
// TestHTTPStatus, TestHTTPBroadcast, TestJSONStatus, TestJSONBroadcast, TestWSConnect, TestWSSend
//--------------------------------------------------------------------------------
// Test the HTTP client
func TestHTTPStatus(t *testing.T) {
testStatus(t, "HTTP")
}
func TestHTTPGenPriv(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testGenPriv(t, "HTTP")
}
func TestHTTPGetAccount(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testGetAccount(t, "HTTP")
}
func TestHTTPSignedTx(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testSignedTx(t, "HTTP")
}
func TestHTTPBroadcastTx(t *testing.T) {
testBroadcastTx(t, "HTTP")
}
func TestHTTPGetStorage(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testGetStorage(t, "HTTP")
}
func TestHTTPCallCode(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testCallCode(t, "HTTP")
}
func TestHTTPCallContract(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testCall(t, "HTTP")
}
func TestHTTPNameReg(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testNameReg(t, "HTTP")
}
//--------------------------------------------------------------------------------
// Test the JSONRPC client
func TestJSONStatus(t *testing.T) {
testStatus(t, "JSONRPC")
}
func TestJSONGenPriv(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testGenPriv(t, "JSONRPC")
}
func TestJSONGetAccount(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testGetAccount(t, "JSONRPC")
}
func TestJSONSignedTx(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testSignedTx(t, "JSONRPC")
}
func TestJSONBroadcastTx(t *testing.T) {
testBroadcastTx(t, "JSONRPC")
}
func TestJSONGetStorage(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testGetStorage(t, "JSONRPC")
}
func TestJSONCallCode(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testCallCode(t, "JSONRPC")
}
func TestJSONCallContract(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testCall(t, "JSONRPC")
}
func TestJSONNameReg(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
testNameReg(t, "JSONRPC")
}

+ 0
- 212
rpc/test/client_ws_test.go View File

@ -1,212 +0,0 @@
package rpctest
import (
"fmt"
"testing"
_ "github.com/tendermint/tendermint/config/tendermint_test"
"github.com/tendermint/tendermint/types"
)
var wsTyp = "JSONRPC"
//--------------------------------------------------------------------------------
// Test the websocket service
// make a simple connection to the server
func TestWSConnect(t *testing.T) {
con := newWSCon(t)
con.Close()
}
// receive a new block message
func TestWSNewBlock(t *testing.T) {
con := newWSCon(t)
eid := types.EventStringNewBlock()
subscribe(t, con, eid)
defer func() {
unsubscribe(t, con, eid)
con.Close()
}()
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error {
fmt.Println("Check:", string(b))
return nil
})
}
// receive a few new block messages in a row, with increasing height
func TestWSBlockchainGrowth(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
con := newWSCon(t)
eid := types.EventStringNewBlock()
subscribe(t, con, eid)
defer func() {
unsubscribe(t, con, eid)
con.Close()
}()
// listen for NewBlock, ensure height increases by 1
unmarshalValidateBlockchain(t, con, eid)
}
// send a transaction and validate the events from listening for both sender and receiver
func TestWSSend(t *testing.T) {
toAddr := user[1].Address
amt := int64(100)
con := newWSCon(t)
eidInput := types.EventStringAccInput(user[0].Address)
eidOutput := types.EventStringAccOutput(toAddr)
subscribe(t, con, eidInput)
subscribe(t, con, eidOutput)
defer func() {
unsubscribe(t, con, eidInput)
unsubscribe(t, con, eidOutput)
con.Close()
}()
waitForEvent(t, con, eidInput, true, func() {
tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt)
broadcastTx(t, wsTyp, tx)
}, unmarshalValidateSend(amt, toAddr))
waitForEvent(t, con, eidOutput, true, func() {}, unmarshalValidateSend(amt, toAddr))
}
// ensure events are only fired once for a given transaction
func TestWSDoubleFire(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
con := newWSCon(t)
eid := types.EventStringAccInput(user[0].Address)
subscribe(t, con, eid)
defer func() {
unsubscribe(t, con, eid)
con.Close()
}()
amt := int64(100)
toAddr := user[1].Address
// broadcast the transaction, wait to hear about it
waitForEvent(t, con, eid, true, func() {
tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt)
broadcastTx(t, wsTyp, tx)
}, func(eid string, b []byte) error {
return nil
})
// but make sure we don't hear about it twice
waitForEvent(t, con, eid, false, func() {
}, func(eid string, b []byte) error {
return nil
})
}
// create a contract, wait for the event, and send it a msg, validate the return
func TestWSCallWait(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
con := newWSCon(t)
eid1 := types.EventStringAccInput(user[0].Address)
subscribe(t, con, eid1)
defer func() {
unsubscribe(t, con, eid1)
con.Close()
}()
amt, gasLim, fee := int64(10000), int64(1000), int64(1000)
code, returnCode, returnVal := simpleContract()
var contractAddr []byte
// wait for the contract to be created
waitForEvent(t, con, eid1, true, func() {
tx := makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee)
receipt := broadcastTx(t, wsTyp, tx)
contractAddr = receipt.ContractAddr
}, unmarshalValidateTx(amt, returnCode))
// susbscribe to the new contract
amt = int64(10001)
eid2 := types.EventStringAccOutput(contractAddr)
subscribe(t, con, eid2)
defer func() {
unsubscribe(t, con, eid2)
}()
// get the return value from a call
data := []byte{0x1}
waitForEvent(t, con, eid2, true, func() {
tx := makeDefaultCallTx(t, wsTyp, contractAddr, data, amt, gasLim, fee)
receipt := broadcastTx(t, wsTyp, tx)
contractAddr = receipt.ContractAddr
}, unmarshalValidateTx(amt, returnVal))
}
// create a contract and send it a msg without waiting. wait for contract event
// and validate return
func TestWSCallNoWait(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
con := newWSCon(t)
amt, gasLim, fee := int64(10000), int64(1000), int64(1000)
code, _, returnVal := simpleContract()
tx := makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee)
receipt := broadcastTx(t, wsTyp, tx)
contractAddr := receipt.ContractAddr
// susbscribe to the new contract
amt = int64(10001)
eid := types.EventStringAccOutput(contractAddr)
subscribe(t, con, eid)
defer func() {
unsubscribe(t, con, eid)
con.Close()
}()
// get the return value from a call
data := []byte{0x1}
waitForEvent(t, con, eid, true, func() {
tx := makeDefaultCallTx(t, wsTyp, contractAddr, data, amt, gasLim, fee)
broadcastTx(t, wsTyp, tx)
}, unmarshalValidateTx(amt, returnVal))
}
// create two contracts, one of which calls the other
func TestWSCallCall(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
con := newWSCon(t)
amt, gasLim, fee := int64(10000), int64(1000), int64(1000)
code, _, returnVal := simpleContract()
txid := new([]byte)
// deploy the two contracts
tx := makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee)
receipt := broadcastTx(t, wsTyp, tx)
contractAddr1 := receipt.ContractAddr
code, _, _ = simpleCallContract(contractAddr1)
tx = makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee)
receipt = broadcastTx(t, wsTyp, tx)
contractAddr2 := receipt.ContractAddr
// susbscribe to the new contracts
amt = int64(10001)
eid1 := types.EventStringAccCall(contractAddr1)
subscribe(t, con, eid1)
defer func() {
unsubscribe(t, con, eid1)
con.Close()
}()
// call contract2, which should call contract1, and wait for ev1
// let the contract get created first
waitForEvent(t, con, eid1, true, func() {
}, func(eid string, b []byte) error {
return nil
})
// call it
waitForEvent(t, con, eid1, true, func() {
tx := makeDefaultCallTx(t, wsTyp, contractAddr2, nil, amt, gasLim, fee)
broadcastTx(t, wsTyp, tx)
*txid = types.TxID(chainID, tx)
}, unmarshalValidateCall(user[0].Address, returnVal, txid))
}

+ 0
- 13
rpc/test/config.go View File

@ -1,13 +0,0 @@
package rpctest
import (
cfg "github.com/tendermint/go-config"
)
var config cfg.Config = nil
func init() {
cfg.OnConfig(func(newConfig cfg.Config) {
config = newConfig
})
}

+ 0
- 282
rpc/test/helpers.go View File

@ -1,282 +0,0 @@
package rpctest
import (
"bytes"
"strconv"
"testing"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
nm "github.com/tendermint/tendermint/node"
"github.com/tendermint/go-p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
cclient "github.com/tendermint/tendermint/rpc/core_client"
"github.com/tendermint/tendermint/types"
)
// global variables for use across all tests
var (
rpcAddr = "127.0.0.1:36657" // Not 46657
requestAddr = "http://" + rpcAddr + "/"
websocketAddr = "ws://" + rpcAddr + "/websocket"
node *nm.Node
mempoolCount = 0
// make keys
user = makeUsers(5)
chainID string
clients = map[string]cclient.Client{
"JSONRPC": cclient.NewClient(requestAddr, "JSONRPC"),
"HTTP": cclient.NewClient(requestAddr, "HTTP"),
}
)
// deterministic account generation, synced with genesis file in config/tendermint_test/config.go
func makeUsers(n int) []*acm.PrivAccount {
accounts := []*acm.PrivAccount{}
for i := 0; i < n; i++ {
secret := ("mysecret" + strconv.Itoa(i))
user := acm.GenPrivAccountFromSecret(secret)
accounts = append(accounts, user)
}
return accounts
}
// create a new node and sleep forever
func newNode(ready chan struct{}) {
// Create & start node
node = nm.NewNode()
l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"))
node.AddListener(l)
node.Start()
// Run the RPC server.
node.StartRPC()
ready <- struct{}{}
// Sleep forever
ch := make(chan struct{})
<-ch
}
// initialize config and create new node
func init() {
chainID = config.GetString("chain_id")
// Save new priv_validator file.
priv := &types.PrivValidator{
Address: user[0].Address,
PubKey: acm.PubKeyEd25519(user[0].PubKey.(acm.PubKeyEd25519)),
PrivKey: acm.PrivKeyEd25519(user[0].PrivKey.(acm.PrivKeyEd25519)),
}
priv.SetFile(config.GetString("priv_validator_file"))
priv.Save()
// TODO: change consensus/state.go timeouts to be shorter
// start a node
ready := make(chan struct{})
go newNode(ready)
<-ready
}
//-------------------------------------------------------------------------------
// some default transaction functions
func makeDefaultSendTx(t *testing.T, typ string, addr []byte, amt int64) *types.SendTx {
nonce := getNonce(t, typ, user[0].Address)
tx := types.NewSendTx()
tx.AddInputWithNonce(user[0].PubKey, amt, nonce+1)
tx.AddOutput(addr, amt)
return tx
}
func makeDefaultSendTxSigned(t *testing.T, typ string, addr []byte, amt int64) *types.SendTx {
tx := makeDefaultSendTx(t, typ, addr, amt)
tx.SignInput(chainID, 0, user[0])
return tx
}
func makeDefaultCallTx(t *testing.T, typ string, addr, code []byte, amt, gasLim, fee int64) *types.CallTx {
nonce := getNonce(t, typ, user[0].Address)
tx := types.NewCallTxWithNonce(user[0].PubKey, addr, code, amt, gasLim, fee, nonce+1)
tx.Sign(chainID, user[0])
return tx
}
func makeDefaultNameTx(t *testing.T, typ string, name, value string, amt, fee int64) *types.NameTx {
nonce := getNonce(t, typ, user[0].Address)
tx := types.NewNameTxWithNonce(user[0].PubKey, name, value, amt, fee, nonce+1)
tx.Sign(chainID, user[0])
return tx
}
//-------------------------------------------------------------------------------
// rpc call wrappers (fail on err)
// get an account's nonce
func getNonce(t *testing.T, typ string, addr []byte) int {
client := clients[typ]
ac, err := client.GetAccount(addr)
if err != nil {
t.Fatal(err)
}
if ac.Account == nil {
return 0
}
return ac.Account.Sequence
}
// get the account
func getAccount(t *testing.T, typ string, addr []byte) *acm.Account {
client := clients[typ]
ac, err := client.GetAccount(addr)
if err != nil {
t.Fatal(err)
}
return ac.Account
}
// sign transaction
func signTx(t *testing.T, typ string, tx types.Tx, privAcc *acm.PrivAccount) types.Tx {
client := clients[typ]
signedTx, err := client.SignTx(tx, []*acm.PrivAccount{privAcc})
if err != nil {
t.Fatal(err)
}
return signedTx.Tx
}
// broadcast transaction
func broadcastTx(t *testing.T, typ string, tx types.Tx) ctypes.Receipt {
client := clients[typ]
rec, err := client.BroadcastTx(tx)
if err != nil {
t.Fatal(err)
}
mempoolCount += 1
return rec.Receipt
}
// dump all storage for an account. currently unused
func dumpStorage(t *testing.T, addr []byte) ctypes.ResultDumpStorage {
client := clients["HTTP"]
resp, err := client.DumpStorage(addr)
if err != nil {
t.Fatal(err)
}
return *resp
}
func getStorage(t *testing.T, typ string, addr, key []byte) []byte {
client := clients[typ]
resp, err := client.GetStorage(addr, key)
if err != nil {
t.Fatal(err)
}
return resp.Value
}
func callCode(t *testing.T, client cclient.Client, fromAddress, code, data, expected []byte) {
resp, err := client.CallCode(fromAddress, code, data)
if err != nil {
t.Fatal(err)
}
ret := resp.Return
// NOTE: we don't flip memory when it comes out of RETURN (?!)
if bytes.Compare(ret, LeftPadWord256(expected).Bytes()) != 0 {
t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected)
}
}
func callContract(t *testing.T, client cclient.Client, fromAddress, toAddress, data, expected []byte) {
resp, err := client.Call(fromAddress, toAddress, data)
if err != nil {
t.Fatal(err)
}
ret := resp.Return
// NOTE: we don't flip memory when it comes out of RETURN (?!)
if bytes.Compare(ret, LeftPadWord256(expected).Bytes()) != 0 {
t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected)
}
}
// get the namereg entry
func getNameRegEntry(t *testing.T, typ string, name string) *types.NameRegEntry {
client := clients[typ]
entry, err := client.GetName(name)
if err != nil {
t.Fatal(err)
}
return entry.Entry
}
//--------------------------------------------------------------------------------
// utility verification function
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)
}
}
// simple contract returns 5 + 6 = 0xb
func simpleContract() ([]byte, []byte, []byte) {
// this is the code we want to run when the contract is called
contractCode := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3}
// the is the code we need to return the contractCode when the contract is initialized
lenCode := len(contractCode)
// push code to the stack
//code := append([]byte{byte(0x60 + lenCode - 1)}, RightPadWord256(contractCode).Bytes()...)
code := append([]byte{0x7f}, RightPadWord256(contractCode).Bytes()...)
// store it in memory
code = append(code, []byte{0x60, 0x0, 0x52}...)
// return whats in memory
//code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...)
code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...)
// return init code, contract code, expected return
return code, contractCode, LeftPadBytes([]byte{0xb}, 32)
}
// simple call contract calls another contract
func simpleCallContract(addr []byte) ([]byte, []byte, []byte) {
gas1, gas2 := byte(0x1), byte(0x1)
value := byte(0x1)
inOff, inSize := byte(0x0), byte(0x0) // no call data
retOff, retSize := byte(0x0), byte(0x20)
// this is the code we want to run (call a contract and return)
contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73}
contractCode = append(contractCode, addr...)
contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...)
// the is the code we need to return; the contractCode when the contract is initialized
// it should copy the code from the input into memory
lenCode := len(contractCode)
memOff := byte(0x0)
inOff = byte(0xc) // length of code before codeContract
length := byte(lenCode)
code := []byte{0x60, length, 0x60, inOff, 0x60, memOff, 0x37}
// return whats in memory
code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...)
code = append(code, contractCode...)
// return init code, contract code, expected return
return code, contractCode, LeftPadBytes([]byte{0xb}, 32)
}

+ 0
- 281
rpc/test/tests.go View File

@ -1,281 +0,0 @@
package rpctest
import (
"bytes"
"fmt"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/types"
"testing"
)
var doNothing = func(eid string, b []byte) error { return nil }
func testStatus(t *testing.T, typ string) {
client := clients[typ]
resp, err := client.Status()
if err != nil {
t.Fatal(err)
}
if resp.NodeInfo.ChainID != chainID {
t.Fatal(fmt.Errorf("ChainID mismatch: got %s expected %s",
resp.NodeInfo.ChainID, chainID))
}
}
func testGenPriv(t *testing.T, typ string) {
client := clients[typ]
privAcc, err := client.GenPrivAccount()
if err != nil {
t.Fatal(err)
}
if len(privAcc.PrivAccount.Address) == 0 {
t.Fatal("Failed to generate an address")
}
}
func testGetAccount(t *testing.T, typ string) {
acc := getAccount(t, typ, user[0].Address)
if acc == nil {
t.Fatalf("Account was nil")
}
if bytes.Compare(acc.Address, user[0].Address) != 0 {
t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address, user[0].Address)
}
}
func testSignedTx(t *testing.T, typ string) {
amt := int64(100)
toAddr := user[1].Address
testOneSignTx(t, typ, toAddr, amt)
toAddr = user[2].Address
testOneSignTx(t, typ, toAddr, amt)
toAddr = user[3].Address
testOneSignTx(t, typ, toAddr, amt)
}
func testOneSignTx(t *testing.T, typ string, addr []byte, amt int64) {
tx := makeDefaultSendTx(t, typ, addr, amt)
tx2 := signTx(t, typ, tx, user[0])
tx2hash := types.TxID(chainID, tx2)
tx.SignInput(chainID, 0, user[0])
txhash := types.TxID(chainID, tx)
if bytes.Compare(txhash, tx2hash) != 0 {
t.Fatal("Got different signatures for signing via rpc vs tx_utils")
}
tx_ := signTx(t, typ, tx, user[0])
tx = tx_.(*types.SendTx)
checkTx(t, user[0].Address, user[0], tx)
}
func testBroadcastTx(t *testing.T, typ string) {
amt := int64(100)
toAddr := user[1].Address
tx := makeDefaultSendTxSigned(t, typ, toAddr, amt)
receipt := broadcastTx(t, typ, tx)
if receipt.CreatesContract > 0 {
t.Fatal("This tx does not create a contract")
}
if len(receipt.TxHash) == 0 {
t.Fatal("Failed to compute tx hash")
}
pool := node.MempoolReactor().Mempool
txs := pool.GetProposalTxs()
if len(txs) != mempoolCount {
t.Fatalf("The mem pool has %d txs. Expected %d", len(txs), mempoolCount)
}
tx2 := txs[mempoolCount-1].(*types.SendTx)
n, err := new(int64), new(error)
buf1, buf2 := new(bytes.Buffer), new(bytes.Buffer)
tx.WriteSignBytes(chainID, buf1, n, err)
tx2.WriteSignBytes(chainID, buf2, n, err)
if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 {
t.Fatal("inconsistent hashes for mempool tx and sent tx")
}
}
func testGetStorage(t *testing.T, typ string) {
con := newWSCon(t)
eid := types.EventStringNewBlock()
subscribe(t, con, eid)
defer func() {
unsubscribe(t, con, eid)
con.Close()
}()
amt, gasLim, fee := int64(1100), int64(1000), int64(1000)
code := []byte{0x60, 0x5, 0x60, 0x1, 0x55}
tx := makeDefaultCallTx(t, typ, nil, code, amt, gasLim, fee)
receipt := broadcastTx(t, typ, tx)
if receipt.CreatesContract == 0 {
t.Fatal("This tx creates a contract")
}
if len(receipt.TxHash) == 0 {
t.Fatal("Failed to compute tx hash")
}
contractAddr := receipt.ContractAddr
if len(contractAddr) == 0 {
t.Fatal("Creates contract but resulting address is empty")
}
// allow it to get mined
waitForEvent(t, con, eid, true, func() {}, doNothing)
mempoolCount = 0
v := getStorage(t, typ, contractAddr, []byte{0x1})
got := LeftPadWord256(v)
expected := LeftPadWord256([]byte{0x5})
if got.Compare(expected) != 0 {
t.Fatalf("Wrong storage value. Got %x, expected %x", got.Bytes(), expected.Bytes())
}
}
func testCallCode(t *testing.T, typ string) {
client := clients[typ]
// add two integers and return the result
code := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3}
data := []byte{}
expected := []byte{0xb}
callCode(t, client, user[0].PubKey.Address(), code, data, expected)
// pass two ints as calldata, add, and return the result
code = []byte{0x60, 0x0, 0x35, 0x60, 0x20, 0x35, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3}
data = append(LeftPadWord256([]byte{0x5}).Bytes(), LeftPadWord256([]byte{0x6}).Bytes()...)
expected = []byte{0xb}
callCode(t, client, user[0].PubKey.Address(), code, data, expected)
}
func testCall(t *testing.T, typ string) {
con := newWSCon(t)
eid := types.EventStringNewBlock()
subscribe(t, con, eid)
defer func() {
unsubscribe(t, con, eid)
con.Close()
}()
client := clients[typ]
// create the contract
amt, gasLim, fee := int64(6969), int64(1000), int64(1000)
code, _, _ := simpleContract()
tx := makeDefaultCallTx(t, typ, nil, code, amt, gasLim, fee)
receipt := broadcastTx(t, typ, tx)
if receipt.CreatesContract == 0 {
t.Fatal("This tx creates a contract")
}
if len(receipt.TxHash) == 0 {
t.Fatal("Failed to compute tx hash")
}
contractAddr := receipt.ContractAddr
if len(contractAddr) == 0 {
t.Fatal("Creates contract but resulting address is empty")
}
// allow it to get mined
waitForEvent(t, con, eid, true, func() {}, doNothing)
mempoolCount = 0
// run a call through the contract
data := []byte{}
expected := []byte{0xb}
callContract(t, client, user[0].PubKey.Address(), contractAddr, data, expected)
}
func testNameReg(t *testing.T, typ string) {
client := clients[typ]
con := newWSCon(t)
types.MinNameRegistrationPeriod = 1
// register a new name, check if its there
// since entries ought to be unique and these run against different clients, we append the typ
name := "ye_old_domain_name_" + typ
data := "if not now, when"
fee := int64(1000)
numDesiredBlocks := int64(2)
amt := fee + numDesiredBlocks*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data)
eid := types.EventStringNameReg(name)
subscribe(t, con, eid)
tx := makeDefaultNameTx(t, typ, name, data, amt, fee)
broadcastTx(t, typ, tx)
// verify the name by both using the event and by checking get_name
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error {
// TODO: unmarshal the response
tx, err := unmarshalResponseNameReg(b)
if err != nil {
return err
}
if tx.Name != name {
t.Fatal(fmt.Sprintf("Err on received event tx.Name: Got %s, expected %s", tx.Name, name))
}
if tx.Data != data {
t.Fatal(fmt.Sprintf("Err on received event tx.Data: Got %s, expected %s", tx.Data, data))
}
return nil
})
mempoolCount = 0
entry := getNameRegEntry(t, typ, name)
if entry.Data != data {
t.Fatal(fmt.Sprintf("Err on entry.Data: Got %s, expected %s", entry.Data, data))
}
if bytes.Compare(entry.Owner, user[0].Address) != 0 {
t.Fatal(fmt.Sprintf("Err on entry.Owner: Got %s, expected %s", entry.Owner, user[0].Address))
}
unsubscribe(t, con, eid)
// for the rest we just use new block event
// since we already tested the namereg event
eid = types.EventStringNewBlock()
subscribe(t, con, eid)
defer func() {
unsubscribe(t, con, eid)
con.Close()
}()
// update the data as the owner, make sure still there
numDesiredBlocks = int64(2)
data = "these are amongst the things I wish to bestow upon the youth of generations come: a safe supply of honey, and a better money. For what else shall they need"
amt = fee + numDesiredBlocks*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data)
tx = makeDefaultNameTx(t, typ, name, data, amt, fee)
broadcastTx(t, typ, tx)
// commit block
waitForEvent(t, con, eid, true, func() {}, doNothing)
mempoolCount = 0
entry = getNameRegEntry(t, typ, name)
if entry.Data != data {
t.Fatal(fmt.Sprintf("Err on entry.Data: Got %s, expected %s", entry.Data, data))
}
// try to update as non owner, should fail
nonce := getNonce(t, typ, user[1].Address)
data2 := "this is not my beautiful house"
tx = types.NewNameTxWithNonce(user[1].PubKey, name, data2, amt, fee, nonce+1)
tx.Sign(chainID, user[1])
_, err := client.BroadcastTx(tx)
if err == nil {
t.Fatal("Expected error on NameTx")
}
// commit block
waitForEvent(t, con, eid, true, func() {}, doNothing)
// now the entry should be expired, so we can update as non owner
_, err = client.BroadcastTx(tx)
waitForEvent(t, con, eid, true, func() {}, doNothing)
mempoolCount = 0
entry = getNameRegEntry(t, typ, name)
if entry.Data != data2 {
t.Fatal(fmt.Sprintf("Error on entry.Data: Got %s, expected %s", entry.Data, data2))
}
if bytes.Compare(entry.Owner, user[1].Address) != 0 {
t.Fatal(fmt.Sprintf("Err on entry.Owner: Got %s, expected %s", entry.Owner, user[1].Address))
}
}

+ 0
- 271
rpc/test/ws_helpers.go View File

@ -1,271 +0,0 @@
package rpctest
import (
"bytes"
"fmt"
"net/http"
"testing"
"time"
"github.com/gorilla/websocket"
"github.com/tendermint/go-wire"
_ "github.com/tendermint/tendermint/config/tendermint_test"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/rpc/types"
"github.com/tendermint/tendermint/types"
)
//--------------------------------------------------------------------------------
// Utilities for testing the websocket service
// create a new connection
func newWSCon(t *testing.T) *websocket.Conn {
dialer := websocket.DefaultDialer
rHeader := http.Header{}
con, r, err := dialer.Dial(websocketAddr, rHeader)
fmt.Println("response", r)
if err != nil {
t.Fatal(err)
}
return con
}
// subscribe to an event
func subscribe(t *testing.T, con *websocket.Conn, eventid string) {
err := con.WriteJSON(rpctypes.RPCRequest{
JSONRPC: "2.0",
ID: "",
Method: "subscribe",
Params: []interface{}{eventid},
})
if err != nil {
t.Fatal(err)
}
}
// unsubscribe from an event
func unsubscribe(t *testing.T, con *websocket.Conn, eventid string) {
err := con.WriteJSON(rpctypes.RPCRequest{
JSONRPC: "2.0",
ID: "",
Method: "unsubscribe",
Params: []interface{}{eventid},
})
if err != nil {
t.Fatal(err)
}
}
// wait for an event; do things that might trigger events, and check them when they are received
// the check function takes an event id and the byte slice read off the ws
func waitForEvent(t *testing.T, con *websocket.Conn, eventid string, dieOnTimeout bool, f func(), check func(string, []byte) error) {
// go routine to wait for webscoket msg
goodCh := make(chan []byte)
errCh := make(chan error)
quitCh := make(chan struct{})
defer close(quitCh)
// Read message
go func() {
for {
_, p, err := con.ReadMessage()
if err != nil {
errCh <- err
break
} else {
// if the event id isnt what we're waiting on
// ignore it
var response ctypes.Response
var err error
wire.ReadJSON(&response, p, &err)
if err != nil {
errCh <- err
break
}
event, ok := response.Result.(*ctypes.ResultEvent)
if ok && event.Event == eventid {
goodCh <- p
break
}
}
}
}()
// do stuff (transactions)
f()
// wait for an event or timeout
timeout := time.NewTimer(10 * time.Second)
select {
case <-timeout.C:
if dieOnTimeout {
con.Close()
t.Fatalf("%s event was not received in time", eventid)
}
// else that's great, we didn't hear the event
// and we shouldn't have
case p := <-goodCh:
if dieOnTimeout {
// message was received and expected
// run the check
err := check(eventid, p)
if err != nil {
t.Fatal(err)
panic(err) // Show the stack trace.
}
} else {
con.Close()
t.Fatalf("%s event was not expected", eventid)
}
case err := <-errCh:
t.Fatal(err)
panic(err) // Show the stack trace.
}
}
//--------------------------------------------------------------------------------
func unmarshalResponseNewBlock(b []byte) (*types.Block, error) {
// unmarshall and assert somethings
var response ctypes.Response
var err error
wire.ReadJSON(&response, b, &err)
if err != nil {
return nil, err
}
if response.Error != "" {
return nil, fmt.Errorf(response.Error)
}
block := response.Result.(*ctypes.ResultEvent).Data.(types.EventDataNewBlock).Block
return block, nil
}
func unmarshalResponseNameReg(b []byte) (*types.NameTx, error) {
// unmarshall and assert somethings
var response ctypes.Response
var err error
wire.ReadJSON(&response, b, &err)
if err != nil {
return nil, err
}
if response.Error != "" {
return nil, fmt.Errorf(response.Error)
}
tx := response.Result.(*ctypes.ResultEvent).Data.(types.EventDataTx).Tx.(*types.NameTx)
return tx, nil
}
func unmarshalValidateBlockchain(t *testing.T, con *websocket.Conn, eid string) {
var initBlockN int
for i := 0; i < 2; i++ {
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error {
block, err := unmarshalResponseNewBlock(b)
if err != nil {
return err
}
if i == 0 {
initBlockN = block.Header.Height
} else {
if block.Header.Height != initBlockN+i {
return fmt.Errorf("Expected block %d, got block %d", i, block.Header.Height)
}
}
return nil
})
}
}
func unmarshalValidateSend(amt int64, toAddr []byte) func(string, []byte) error {
return func(eid string, b []byte) error {
// unmarshal and assert correctness
var response ctypes.Response
var err error
wire.ReadJSON(&response, b, &err)
if err != nil {
return err
}
if response.Error != "" {
return fmt.Errorf(response.Error)
}
if eid != response.Result.(*ctypes.ResultEvent).Event {
return fmt.Errorf("Eventid is not correct. Got %s, expected %s", response.Result.(*ctypes.ResultEvent).Event, eid)
}
tx := response.Result.(*ctypes.ResultEvent).Data.(types.EventDataTx).Tx.(*types.SendTx)
if !bytes.Equal(tx.Inputs[0].Address, user[0].Address) {
return fmt.Errorf("Senders do not match up! Got %x, expected %x", tx.Inputs[0].Address, user[0].Address)
}
if tx.Inputs[0].Amount != amt {
return fmt.Errorf("Amt does not match up! Got %d, expected %d", tx.Inputs[0].Amount, amt)
}
if !bytes.Equal(tx.Outputs[0].Address, toAddr) {
return fmt.Errorf("Receivers do not match up! Got %x, expected %x", tx.Outputs[0].Address, user[0].Address)
}
return nil
}
}
func unmarshalValidateTx(amt int64, returnCode []byte) func(string, []byte) error {
return func(eid string, b []byte) error {
// unmarshall and assert somethings
var response ctypes.Response
var err error
wire.ReadJSON(&response, b, &err)
if err != nil {
return err
}
if response.Error != "" {
return fmt.Errorf(response.Error)
}
var data = response.Result.(*ctypes.ResultEvent).Data.(types.EventDataTx)
if data.Exception != "" {
return fmt.Errorf(data.Exception)
}
tx := data.Tx.(*types.CallTx)
if !bytes.Equal(tx.Input.Address, user[0].Address) {
return fmt.Errorf("Senders do not match up! Got %x, expected %x",
tx.Input.Address, user[0].Address)
}
if tx.Input.Amount != amt {
return fmt.Errorf("Amt does not match up! Got %d, expected %d",
tx.Input.Amount, amt)
}
ret := data.Return
if !bytes.Equal(ret, returnCode) {
return fmt.Errorf("Tx did not return correctly. Got %x, expected %x", ret, returnCode)
}
return nil
}
}
func unmarshalValidateCall(origin, returnCode []byte, txid *[]byte) func(string, []byte) error {
return func(eid string, b []byte) error {
// unmarshall and assert somethings
var response ctypes.Response
var err error
wire.ReadJSON(&response, b, &err)
if err != nil {
return err
}
if response.Error != "" {
return fmt.Errorf(response.Error)
}
var data = response.Result.(*ctypes.ResultEvent).Data.(types.EventDataCall)
if data.Exception != "" {
return fmt.Errorf(data.Exception)
}
if !bytes.Equal(data.Origin, origin) {
return fmt.Errorf("Origin does not match up! Got %x, expected %x",
data.Origin, origin)
}
ret := data.Return
if !bytes.Equal(ret, returnCode) {
return fmt.Errorf("Call did not return correctly. Got %x, expected %x", ret, returnCode)
}
if !bytes.Equal(data.TxID, *txid) {
return fmt.Errorf("TxIDs do not match up! Got %x, expected %x",
data.TxID, *txid)
}
return nil
}
}

+ 0
- 293
state/block_cache.go View File

@ -1,293 +0,0 @@
package state
import (
"bytes"
"sort"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
dbm "github.com/tendermint/go-db"
"github.com/tendermint/go-merkle"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/go-wire"
)
func makeStorage(db dbm.DB, root []byte) merkle.Tree {
storage := merkle.NewIAVLTree(
wire.BasicCodec,
wire.BasicCodec,
1024,
db,
)
storage.Load(root)
return storage
}
// The blockcache helps prevent unnecessary IAVLTree updates and garbage generation.
type BlockCache struct {
db dbm.DB
backend *State
accounts map[string]accountInfo
storages map[Tuple256]storageInfo
names map[string]nameInfo
}
func NewBlockCache(backend *State) *BlockCache {
return &BlockCache{
db: backend.DB,
backend: backend,
accounts: make(map[string]accountInfo),
storages: make(map[Tuple256]storageInfo),
names: make(map[string]nameInfo),
}
}
func (cache *BlockCache) State() *State {
return cache.backend
}
//-------------------------------------
// BlockCache.account
func (cache *BlockCache) GetAccount(addr []byte) *acm.Account {
acc, _, removed, _ := cache.accounts[string(addr)].unpack()
if removed {
return nil
} else if acc != nil {
return acc
} else {
acc = cache.backend.GetAccount(addr)
cache.accounts[string(addr)] = accountInfo{acc, nil, false, false}
return acc
}
}
func (cache *BlockCache) UpdateAccount(acc *acm.Account) {
addr := acc.Address
_, storage, removed, _ := cache.accounts[string(addr)].unpack()
if removed {
PanicSanity("UpdateAccount on a removed account")
}
cache.accounts[string(addr)] = accountInfo{acc, storage, false, true}
}
func (cache *BlockCache) RemoveAccount(addr []byte) {
_, _, removed, _ := cache.accounts[string(addr)].unpack()
if removed {
PanicSanity("RemoveAccount on a removed account")
}
cache.accounts[string(addr)] = accountInfo{nil, nil, true, false}
}
// BlockCache.account
//-------------------------------------
// BlockCache.storage
func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) {
// Check cache
info, ok := cache.storages[Tuple256{addr, key}]
if ok {
return info.value
}
// Get or load storage
acc, storage, removed, dirty := cache.accounts[string(addr.Postfix(20))].unpack()
if removed {
PanicSanity("GetStorage() on removed account")
}
if acc != nil && storage == nil {
storage = makeStorage(cache.db, acc.StorageRoot)
cache.accounts[string(addr.Postfix(20))] = accountInfo{acc, storage, false, dirty}
} else if acc == nil {
return Zero256
}
// Load and set cache
_, val_ := storage.Get(key.Bytes())
value = Zero256
if val_ != nil {
value = LeftPadWord256(val_.([]byte))
}
cache.storages[Tuple256{addr, key}] = storageInfo{value, false}
return value
}
// NOTE: Set value to zero to removed from the trie.
func (cache *BlockCache) SetStorage(addr Word256, key Word256, value Word256) {
_, _, removed, _ := cache.accounts[string(addr.Postfix(20))].unpack()
if removed {
PanicSanity("SetStorage() on a removed account")
}
cache.storages[Tuple256{addr, key}] = storageInfo{value, true}
}
// BlockCache.storage
//-------------------------------------
// BlockCache.names
func (cache *BlockCache) GetNameRegEntry(name string) *types.NameRegEntry {
entry, removed, _ := cache.names[name].unpack()
if removed {
return nil
} else if entry != nil {
return entry
} else {
entry = cache.backend.GetNameRegEntry(name)
cache.names[name] = nameInfo{entry, false, false}
return entry
}
}
func (cache *BlockCache) UpdateNameRegEntry(entry *types.NameRegEntry) {
name := entry.Name
cache.names[name] = nameInfo{entry, false, true}
}
func (cache *BlockCache) RemoveNameRegEntry(name string) {
_, removed, _ := cache.names[name].unpack()
if removed {
PanicSanity("RemoveNameRegEntry on a removed entry")
}
cache.names[name] = nameInfo{nil, true, false}
}
// BlockCache.names
//-------------------------------------
// CONTRACT the updates are in deterministic order.
func (cache *BlockCache) Sync() {
// Determine order for storage updates
// The address comes first so it'll be grouped.
storageKeys := make([]Tuple256, 0, len(cache.storages))
for keyTuple := range cache.storages {
storageKeys = append(storageKeys, keyTuple)
}
Tuple256Slice(storageKeys).Sort()
// Update storage for all account/key.
// Later we'll iterate over all the users and save storage + update storage root.
var (
curAddr Word256
curAcc *acm.Account
curAccRemoved bool
curStorage merkle.Tree
)
for _, storageKey := range storageKeys {
addr, key := Tuple256Split(storageKey)
if addr != curAddr || curAcc == nil {
acc, storage, removed, _ := cache.accounts[string(addr.Postfix(20))].unpack()
if !removed && storage == nil {
storage = makeStorage(cache.db, acc.StorageRoot)
}
curAddr = addr
curAcc = acc
curAccRemoved = removed
curStorage = storage
}
if curAccRemoved {
continue
}
value, dirty := cache.storages[storageKey].unpack()
if !dirty {
continue
}
if value.IsZero() {
curStorage.Remove(key.Bytes())
} else {
curStorage.Set(key.Bytes(), value.Bytes())
cache.accounts[string(addr.Postfix(20))] = accountInfo{curAcc, curStorage, false, true}
}
}
// Determine order for accounts
addrStrs := []string{}
for addrStr := range cache.accounts {
addrStrs = append(addrStrs, addrStr)
}
sort.Strings(addrStrs)
// Update or delete accounts.
for _, addrStr := range addrStrs {
acc, storage, removed, dirty := cache.accounts[addrStr].unpack()
if removed {
removed := cache.backend.RemoveAccount([]byte(addrStr))
if !removed {
PanicCrisis(Fmt("Could not remove account to be removed: %X", acc.Address))
}
} else {
if acc == nil {
continue
}
if storage != nil {
newStorageRoot := storage.Save()
if !bytes.Equal(newStorageRoot, acc.StorageRoot) {
acc.StorageRoot = newStorageRoot
dirty = true
}
}
if dirty {
cache.backend.UpdateAccount(acc)
}
}
}
// Determine order for names
// note names may be of any length less than some limit
nameStrs := []string{}
for nameStr := range cache.names {
nameStrs = append(nameStrs, nameStr)
}
sort.Strings(nameStrs)
// Update or delete names.
for _, nameStr := range nameStrs {
entry, removed, dirty := cache.names[nameStr].unpack()
if removed {
removed := cache.backend.RemoveNameRegEntry(nameStr)
if !removed {
PanicCrisis(Fmt("Could not remove namereg entry to be removed: %s", nameStr))
}
} else {
if entry == nil {
continue
}
if dirty {
cache.backend.UpdateNameRegEntry(entry)
}
}
}
}
//-----------------------------------------------------------------------------
type accountInfo struct {
account *acm.Account
storage merkle.Tree
removed bool
dirty bool
}
func (accInfo accountInfo) unpack() (*acm.Account, merkle.Tree, bool, bool) {
return accInfo.account, accInfo.storage, accInfo.removed, accInfo.dirty
}
type storageInfo struct {
value Word256
dirty bool
}
func (stjInfo storageInfo) unpack() (Word256, bool) {
return stjInfo.value, stjInfo.dirty
}
type nameInfo struct {
name *types.NameRegEntry
removed bool
dirty bool
}
func (nInfo nameInfo) unpack() (*types.NameRegEntry, bool, bool) {
return nInfo.name, nInfo.removed, nInfo.dirty
}

+ 0
- 18
state/common.go View File

@ -1,18 +0,0 @@
package state
import (
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/vm"
)
type AccountGetter interface {
GetAccount(addr []byte) *acm.Account
}
type VMAccountState interface {
GetAccount(addr Word256) *vm.Account
UpdateAccount(acc *vm.Account)
RemoveAccount(acc *vm.Account)
CreateAccount(creator *vm.Account) *vm.Account
}

+ 16
- 892
state/execution.go View File

@ -3,14 +3,10 @@ package state
import (
"bytes"
"errors"
"fmt"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/events"
ptypes "github.com/tendermint/tendermint/permission/types" // for GlobalPermissionAddress ...
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/vm"
)
// NOTE: If an error occurs during block execution, state will be left
@ -45,11 +41,11 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade
return errors.New("Block at height 1 (first block) should have no LastValidation precommits")
}
} else {
if len(block.LastValidation.Precommits) != s.LastBondedValidators.Size() {
if len(block.LastValidation.Precommits) != s.LastValidators.Size() {
return errors.New(Fmt("Invalid block validation size. Expected %v, got %v",
s.LastBondedValidators.Size(), len(block.LastValidation.Precommits)))
s.LastValidators.Size(), len(block.LastValidation.Precommits)))
}
err := s.LastBondedValidators.VerifyValidation(
err := s.LastValidators.VerifyValidation(
s.ChainID, s.LastBlockHash, s.LastBlockParts, block.Height-1, block.LastValidation)
if err != nil {
return err
@ -61,74 +57,34 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade
if precommit == nil {
continue
}
_, val := s.LastBondedValidators.GetByIndex(i)
_, val := s.LastValidators.GetByIndex(i)
if val == nil {
PanicCrisis(Fmt("Failed to fetch validator at index %v", i))
}
if _, val_ := s.BondedValidators.GetByAddress(val.Address); val_ != nil {
if _, val_ := s.Validators.GetByAddress(val.Address); val_ != nil {
val_.LastCommitHeight = block.Height - 1
updated := s.BondedValidators.Update(val_)
updated := s.Validators.Update(val_)
if !updated {
PanicCrisis("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 {
PanicCrisis("Failed to update unbonding validator LastCommitHeight")
PanicCrisis("Failed to update validator LastCommitHeight")
}
} else {
PanicCrisis("Could not find validator")
}
}
// Remember LastBondedValidators
s.LastBondedValidators = s.BondedValidators.Copy()
// Create BlockCache to cache changes to state.
blockCache := NewBlockCache(s)
// Remember LastValidators
s.LastValidators = s.Validators.Copy()
// Execute each tx
for _, tx := range block.Data.Txs {
err := ExecTx(blockCache, tx, true, s.evc)
err := ExecTx(s, tx, 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 := []*types.Validator{}
s.UnbondingValidators.Iterate(func(index int, val *types.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 := []*types.Validator{}
s.BondedValidators.Iterate(func(index int, val *types.Validator) bool {
lastActivityHeight := MaxInt(val.BondHeight, val.LastCommitHeight)
if lastActivityHeight+validatorTimeoutBlocks < block.Height {
log.Notice("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.Validators.IncrementAccum(1)
s.LastBlockHeight = block.Height
s.LastBlockHash = block.Hash()
s.LastBlockParts = blockPartsHeader
@ -136,849 +92,17 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade
return nil
}
// The accounts from the TxInputs must either already have
// acm.PubKey.(type) != nil, (it must be known),
// or it must be specified in the TxInput. If redeclared,
// the TxInput is modified and input.PubKey set to nil.
func getInputs(state AccountGetter, ins []*types.TxInput) (map[string]*acm.Account, error) {
accounts := map[string]*acm.Account{}
for _, in := range ins {
// Account shouldn't be duplicated
if _, ok := accounts[string(in.Address)]; ok {
return nil, types.ErrTxDuplicateAddress
}
acc := state.GetAccount(in.Address)
if acc == nil {
return nil, types.ErrTxInvalidAddress
}
// PubKey should be present in either "account" or "in"
if err := checkInputPubKey(acc, in); err != nil {
return nil, err
}
accounts[string(in.Address)] = acc
}
return accounts, nil
}
func getOrMakeOutputs(state AccountGetter, accounts map[string]*acm.Account, outs []*types.TxOutput) (map[string]*acm.Account, error) {
if accounts == nil {
accounts = make(map[string]*acm.Account)
}
// we should err if an account is being created but the inputs don't have permission
var checkedCreatePerms bool
for _, out := range outs {
// Account shouldn't be duplicated
if _, ok := accounts[string(out.Address)]; ok {
return nil, types.ErrTxDuplicateAddress
}
acc := state.GetAccount(out.Address)
// output account may be nil (new)
if acc == nil {
if !checkedCreatePerms {
if !hasCreateAccountPermission(state, accounts) {
return nil, fmt.Errorf("At least one input does not have permission to create accounts")
}
checkedCreatePerms = true
}
acc = &acm.Account{
Address: out.Address,
PubKey: nil,
Sequence: 0,
Balance: 0,
Permissions: ptypes.ZeroAccountPermissions,
}
}
accounts[string(out.Address)] = acc
}
return accounts, nil
}
func checkInputPubKey(acc *acm.Account, in *types.TxInput) error {
if acc.PubKey == nil {
if in.PubKey == nil {
return types.ErrTxUnknownPubKey
}
if !bytes.Equal(in.PubKey.Address(), acc.Address) {
return types.ErrTxInvalidPubKey
}
acc.PubKey = in.PubKey
} else {
in.PubKey = nil
}
return nil
}
func validateInputs(accounts map[string]*acm.Account, signBytes []byte, ins []*types.TxInput) (total int64, err error) {
for _, in := range ins {
acc := accounts[string(in.Address)]
if acc == nil {
PanicSanity("validateInputs() expects account in accounts")
}
err = validateInput(acc, signBytes, in)
if err != nil {
return
}
// Good. Add amount to total
total += in.Amount
}
return total, nil
}
func validateInput(acc *acm.Account, signBytes []byte, in *types.TxInput) (err error) {
// Check TxInput basic
if err := in.ValidateBasic(); err != nil {
return err
}
// Check signatures
if !acc.PubKey.VerifyBytes(signBytes, in.Signature) {
return types.ErrTxInvalidSignature
}
// Check sequences
if acc.Sequence+1 != in.Sequence {
return types.ErrTxInvalidSequence{
Got: in.Sequence,
Expected: acc.Sequence + 1,
}
}
// Check amount
if acc.Balance < in.Amount {
return types.ErrTxInsufficientFunds
}
return nil
}
func validateOutputs(outs []*types.TxOutput) (total int64, err error) {
for _, out := range outs {
// Check TxOutput basic
if err := out.ValidateBasic(); err != nil {
return 0, err
}
// Good. Add amount to total
total += out.Amount
}
return total, nil
}
func adjustByInputs(accounts map[string]*acm.Account, ins []*types.TxInput) {
for _, in := range ins {
acc := accounts[string(in.Address)]
if acc == nil {
PanicSanity("adjustByInputs() expects account in accounts")
}
if acc.Balance < in.Amount {
PanicSanity("adjustByInputs() expects sufficient funds")
}
acc.Balance -= in.Amount
acc.Sequence += 1
}
}
func adjustByOutputs(accounts map[string]*acm.Account, outs []*types.TxOutput) {
for _, out := range outs {
acc := accounts[string(out.Address)]
if acc == nil {
PanicSanity("adjustByOutputs() expects account in accounts")
}
acc.Balance += out.Amount
}
}
// 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) {
func ExecTx(s *State, tx types.Tx, 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, ""})
}
//fees := int64(0)
//_s := blockCache.State() // hack to access validators and block height
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
}
}
//---------------------------------------------------------------
// Get permission on an account or fall back to global value
func HasPermission(state AccountGetter, acc *acm.Account, perm ptypes.PermFlag) bool {
if perm > ptypes.AllPermFlags {
PanicSanity("Checking an unknown permission in state should never happen")
}
if acc == nil {
// TODO
// this needs to fall back to global or do some other specific things
// eg. a bondAcc may be nil and so can only bond if global bonding is true
}
permString := ptypes.PermFlagToString(perm)
v, err := acc.Permissions.Base.Get(perm)
if _, ok := err.(ptypes.ErrValueNotSet); ok {
if state == nil {
PanicSanity("All known global permissions should be set!")
}
log.Info("Permission for account is not set. Querying GlobalPermissionsAddress", "perm", permString)
return HasPermission(nil, state.GetAccount(ptypes.GlobalPermissionsAddress), perm)
} else if v {
log.Info("Account has permission", "address", Fmt("%X", acc.Address), "perm", permString)
} else {
log.Info("Account does not have permission", "address", Fmt("%X", acc.Address), "perm", permString)
}
return v
}
// TODO: for debug log the failed accounts
func hasSendPermission(state AccountGetter, accs map[string]*acm.Account) bool {
for _, acc := range accs {
if !HasPermission(state, acc, ptypes.Send) {
return false
}
}
return true
}
func hasNamePermission(state AccountGetter, acc *acm.Account) bool {
return HasPermission(state, acc, ptypes.Name)
}
func hasCallPermission(state AccountGetter, acc *acm.Account) bool {
return HasPermission(state, acc, ptypes.Call)
}
func hasCreateContractPermission(state AccountGetter, acc *acm.Account) bool {
return HasPermission(state, acc, ptypes.CreateContract)
}
func hasCreateAccountPermission(state AccountGetter, accs map[string]*acm.Account) bool {
for _, acc := range accs {
if !HasPermission(state, acc, ptypes.CreateAccount) {
return false
}
}
return true
}
func hasBondPermission(state AccountGetter, acc *acm.Account) bool {
return HasPermission(state, acc, ptypes.Bond)
}
// XXX Query ledger application
return nil
func hasBondOrSendPermission(state AccountGetter, accs map[string]*acm.Account) bool {
for _, acc := range accs {
if !HasPermission(state, acc, ptypes.Bond) {
if !HasPermission(state, acc, ptypes.Send) {
return false
}
}
}
return true
}
//-----------------------------------------------------------------------------


+ 0
- 87
state/genesis_test.go View File

@ -1,87 +0,0 @@
package state
import (
"bytes"
"encoding/hex"
"fmt"
"testing"
tdb "github.com/tendermint/go-db"
ptypes "github.com/tendermint/tendermint/permission/types"
. "github.com/tendermint/tendermint/state/types"
)
var chain_id = "lone_ranger"
var addr1, _ = hex.DecodeString("964B1493BBE3312278B7DEB94C39149F7899A345")
var send1, name1, call1 = 1, 1, 0
var perms, setbit = 66, 70
var accName = "me"
var roles1 = []string{"master", "universal-ruler"}
var amt1 int64 = 1000000
var g1 = fmt.Sprintf(`
{
"chain_id":"%s",
"accounts": [
{
"address": "%X",
"amount": %d,
"name": "%s",
"permissions": {
"base": {
"perms": %d,
"set": %d
},
"roles": [
"%s",
"%s"
]
}
}
],
"validators": [
{
"amount": 100000000,
"pub_key": [1,"F6C79CF0CB9D66B677988BCB9B8EADD9A091CD465A60542A8AB85476256DBA92"],
"unbond_to": [
{
"address": "964B1493BBE3312278B7DEB94C39149F7899A345",
"amount": 10000000
}
]
}
]
}
`, chain_id, addr1, amt1, accName, perms, setbit, roles1[0], roles1[1])
func TestGenesisReadable(t *testing.T) {
genDoc := GenesisDocFromJSON([]byte(g1))
if genDoc.ChainID != chain_id {
t.Fatalf("Incorrect chain id. Got %d, expected %d\n", genDoc.ChainID, chain_id)
}
acc := genDoc.Accounts[0]
if bytes.Compare(acc.Address, addr1) != 0 {
t.Fatalf("Incorrect address for account. Got %X, expected %X\n", acc.Address, addr1)
}
if acc.Amount != amt1 {
t.Fatalf("Incorrect amount for account. Got %d, expected %d\n", acc.Amount, amt1)
}
if acc.Name != accName {
t.Fatalf("Incorrect name for account. Got %s, expected %s\n", acc.Name, accName)
}
perm, _ := acc.Permissions.Base.Get(ptypes.Send)
if perm != (send1 > 0) {
t.Fatalf("Incorrect permission for send. Got %v, expected %v\n", perm, send1 > 0)
}
}
func TestGenesisMakeState(t *testing.T) {
genDoc := GenesisDocFromJSON([]byte(g1))
db := tdb.NewMemDB()
st := MakeGenesisState(db, genDoc)
acc := st.GetAccount(addr1)
v, _ := acc.Permissions.Base.Get(ptypes.Send)
if v != (send1 > 0) {
t.Fatalf("Incorrect permission for send. Got %v, expected %v\n", v, send1 > 0)
}
}

+ 0
- 1265
state/permissions_test.go
File diff suppressed because it is too large
View File


+ 41
- 350
state/state.go View File

@ -1,47 +1,34 @@
package state
import (
"bytes"
"io"
"io/ioutil"
"time"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
dbm "github.com/tendermint/go-db"
"github.com/tendermint/tendermint/events"
"github.com/tendermint/go-merkle"
ptypes "github.com/tendermint/tendermint/permission/types"
. "github.com/tendermint/tendermint/state/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/events"
"github.com/tendermint/tendermint/types"
)
var (
stateKey = []byte("stateKey")
minBondAmount = int64(1) // TODO adjust
defaultAccountsCacheCapacity = 1000 // TODO adjust
unbondingPeriodBlocks = int(60 * 24 * 365) // TODO probably better to make it time based.
validatorTimeoutBlocks = int(10) // TODO adjust
stateKey = []byte("stateKey")
)
//-----------------------------------------------------------------------------
// NOTE: not goroutine-safe.
type State struct {
DB dbm.DB
ChainID string
LastBlockHeight int
LastBlockHash []byte
LastBlockParts types.PartSetHeader
LastBlockTime time.Time
BondedValidators *types.ValidatorSet
LastBondedValidators *types.ValidatorSet
UnbondingValidators *types.ValidatorSet
accounts merkle.Tree // Shouldn't be accessed directly.
validatorInfos merkle.Tree // Shouldn't be accessed directly.
nameReg merkle.Tree // Shouldn't be accessed directly.
DB dbm.DB
ChainID string
LastBlockHeight int
LastBlockHash []byte
LastBlockParts types.PartSetHeader
LastBlockTime time.Time
Validators *types.ValidatorSet
LastValidators *types.ValidatorSet
evc events.Fireable // typically an events.EventCache
}
@ -58,18 +45,8 @@ func LoadState(db dbm.DB) *State {
s.LastBlockHash = wire.ReadByteSlice(r, n, err)
s.LastBlockParts = wire.ReadBinary(types.PartSetHeader{}, r, n, err).(types.PartSetHeader)
s.LastBlockTime = wire.ReadTime(r, n, err)
s.BondedValidators = wire.ReadBinary(&types.ValidatorSet{}, r, n, err).(*types.ValidatorSet)
s.LastBondedValidators = wire.ReadBinary(&types.ValidatorSet{}, r, n, err).(*types.ValidatorSet)
s.UnbondingValidators = wire.ReadBinary(&types.ValidatorSet{}, r, n, err).(*types.ValidatorSet)
accountsHash := wire.ReadByteSlice(r, n, err)
s.accounts = merkle.NewIAVLTree(wire.BasicCodec, acm.AccountCodec, defaultAccountsCacheCapacity, db)
s.accounts.Load(accountsHash)
validatorInfosHash := wire.ReadByteSlice(r, n, err)
s.validatorInfos = merkle.NewIAVLTree(wire.BasicCodec, types.ValidatorInfoCodec, 0, db)
s.validatorInfos.Load(validatorInfosHash)
nameRegHash := wire.ReadByteSlice(r, n, err)
s.nameReg = merkle.NewIAVLTree(wire.BasicCodec, NameRegCodec, 0, db)
s.nameReg.Load(nameRegHash)
s.Validators = wire.ReadBinary(&types.ValidatorSet{}, r, n, err).(*types.ValidatorSet)
s.LastValidators = wire.ReadBinary(&types.ValidatorSet{}, r, n, err).(*types.ValidatorSet)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
Exit(Fmt("Data has been corrupted or its spec has changed: %v\n", *err))
@ -80,21 +57,14 @@ func LoadState(db dbm.DB) *State {
}
func (s *State) Save() {
s.accounts.Save()
s.validatorInfos.Save()
s.nameReg.Save()
buf, n, err := new(bytes.Buffer), new(int64), new(error)
wire.WriteString(s.ChainID, buf, n, err)
wire.WriteVarint(s.LastBlockHeight, buf, n, err)
wire.WriteByteSlice(s.LastBlockHash, buf, n, err)
wire.WriteBinary(s.LastBlockParts, buf, n, err)
wire.WriteTime(s.LastBlockTime, buf, n, err)
wire.WriteBinary(s.BondedValidators, buf, n, err)
wire.WriteBinary(s.LastBondedValidators, buf, n, err)
wire.WriteBinary(s.UnbondingValidators, buf, n, err)
wire.WriteByteSlice(s.accounts.Hash(), buf, n, err)
wire.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
wire.WriteByteSlice(s.nameReg.Hash(), buf, n, err)
wire.WriteBinary(s.Validators, buf, n, err)
wire.WriteBinary(s.LastValidators, buf, n, err)
if *err != nil {
PanicCrisis(*err)
}
@ -106,30 +76,22 @@ func (s *State) Save() {
// as if State were copied by value.
func (s *State) Copy() *State {
return &State{
DB: s.DB,
ChainID: s.ChainID,
LastBlockHeight: s.LastBlockHeight,
LastBlockHash: s.LastBlockHash,
LastBlockParts: s.LastBlockParts,
LastBlockTime: s.LastBlockTime,
BondedValidators: s.BondedValidators.Copy(), // TODO remove need for Copy() here.
LastBondedValidators: s.LastBondedValidators.Copy(), // That is, make updates to the validator set
UnbondingValidators: s.UnbondingValidators.Copy(), // copy the valSet lazily.
accounts: s.accounts.Copy(),
validatorInfos: s.validatorInfos.Copy(),
nameReg: s.nameReg.Copy(),
evc: nil,
DB: s.DB,
ChainID: s.ChainID,
LastBlockHeight: s.LastBlockHeight,
LastBlockHash: s.LastBlockHash,
LastBlockParts: s.LastBlockParts,
LastBlockTime: s.LastBlockTime,
Validators: s.Validators.Copy(), // TODO remove need for Copy() here.
LastValidators: s.LastValidators.Copy(), // That is, make updates to the validator set
evc: nil,
}
}
// Returns a hash that represents the state data, excluding Last*
func (s *State) Hash() []byte {
return merkle.SimpleHashFromMap(map[string]interface{}{
"BondedValidators": s.BondedValidators,
"UnbondingValidators": s.UnbondingValidators,
"Accounts": s.accounts,
"ValidatorInfos": s.validatorInfos,
"NameRegistry": s.nameReg,
"Validators": s.Validators,
})
}
@ -150,213 +112,6 @@ func (s *State) SetDB(db dbm.DB) {
s.DB = db
}
//-------------------------------------
// State.params
func (s *State) GetGasLimit() int64 {
return 1000000 // TODO
}
// State.params
//-------------------------------------
// State.accounts
// Returns nil if account does not exist with given address.
// The returned Account is a copy, so mutating it
// has no side effects.
// Implements Statelike
func (s *State) GetAccount(address []byte) *acm.Account {
_, acc := s.accounts.Get(address)
if acc == nil {
return nil
}
return acc.(*acm.Account).Copy()
}
// The account is copied before setting, so mutating it
// afterwards has no side effects.
// Implements Statelike
func (s *State) UpdateAccount(account *acm.Account) bool {
return s.accounts.Set(account.Address, account.Copy())
}
// Implements Statelike
func (s *State) RemoveAccount(address []byte) bool {
_, removed := s.accounts.Remove(address)
return removed
}
// The returned Account is a copy, so mutating it
// has no side effects.
func (s *State) GetAccounts() merkle.Tree {
return s.accounts.Copy()
}
// Set the accounts tree
func (s *State) SetAccounts(accounts merkle.Tree) {
s.accounts = accounts
}
// State.accounts
//-------------------------------------
// State.validators
// The returned ValidatorInfo is a copy, so mutating it
// has no side effects.
func (s *State) GetValidatorInfo(address []byte) *types.ValidatorInfo {
_, valInfo := s.validatorInfos.Get(address)
if valInfo == nil {
return nil
}
return valInfo.(*types.ValidatorInfo).Copy()
}
// Returns false if new, true if updated.
// The valInfo is copied before setting, so mutating it
// afterwards has no side effects.
func (s *State) SetValidatorInfo(valInfo *types.ValidatorInfo) (updated bool) {
return s.validatorInfos.Set(valInfo.Address, valInfo.Copy())
}
func (s *State) GetValidatorInfos() merkle.Tree {
return s.validatorInfos.Copy()
}
func (s *State) unbondValidator(val *types.Validator) {
// Move validator to UnbondingValidators
val, removed := s.BondedValidators.Remove(val.Address)
if !removed {
PanicCrisis("Couldn't remove validator for unbonding")
}
val.UnbondHeight = s.LastBlockHeight + 1
added := s.UnbondingValidators.Add(val)
if !added {
PanicCrisis("Couldn't add validator for unbonding")
}
}
func (s *State) rebondValidator(val *types.Validator) {
// Move validator to BondingValidators
val, removed := s.UnbondingValidators.Remove(val.Address)
if !removed {
PanicCrisis("Couldn't remove validator for rebonding")
}
val.BondHeight = s.LastBlockHeight + 1
added := s.BondedValidators.Add(val)
if !added {
PanicCrisis("Couldn't add validator for rebonding")
}
}
func (s *State) releaseValidator(val *types.Validator) {
// Update validatorInfo
valInfo := s.GetValidatorInfo(val.Address)
if valInfo == nil {
PanicSanity("Couldn't find validatorInfo for release")
}
valInfo.ReleasedHeight = s.LastBlockHeight + 1
s.SetValidatorInfo(valInfo)
// Send coins back to UnbondTo outputs
accounts, err := getOrMakeOutputs(s, nil, valInfo.UnbondTo)
if err != nil {
PanicSanity("Couldn't get or make unbondTo accounts")
}
adjustByOutputs(accounts, valInfo.UnbondTo)
for _, acc := range accounts {
s.UpdateAccount(acc)
}
// Remove validator from UnbondingValidators
_, removed := s.UnbondingValidators.Remove(val.Address)
if !removed {
PanicCrisis("Couldn't remove validator for release")
}
}
func (s *State) destroyValidator(val *types.Validator) {
// Update validatorInfo
valInfo := s.GetValidatorInfo(val.Address)
if valInfo == nil {
PanicSanity("Couldn't find validatorInfo for release")
}
valInfo.DestroyedHeight = s.LastBlockHeight + 1
valInfo.DestroyedAmount = val.VotingPower
s.SetValidatorInfo(valInfo)
// Remove validator
_, removed := s.BondedValidators.Remove(val.Address)
if !removed {
_, removed := s.UnbondingValidators.Remove(val.Address)
if !removed {
PanicCrisis("Couldn't remove validator for destruction")
}
}
}
// Set the validator infos tree
func (s *State) SetValidatorInfos(validatorInfos merkle.Tree) {
s.validatorInfos = validatorInfos
}
// State.validators
//-------------------------------------
// State.storage
func (s *State) LoadStorage(hash []byte) (storage merkle.Tree) {
storage = merkle.NewIAVLTree(wire.BasicCodec, wire.BasicCodec, 1024, s.DB)
storage.Load(hash)
return storage
}
// State.storage
//-------------------------------------
// State.nameReg
func (s *State) GetNameRegEntry(name string) *types.NameRegEntry {
_, value := s.nameReg.Get(name)
if value == nil {
return nil
}
entry := value.(*types.NameRegEntry)
return entry.Copy()
}
func (s *State) UpdateNameRegEntry(entry *types.NameRegEntry) bool {
return s.nameReg.Set(entry.Name, entry)
}
func (s *State) RemoveNameRegEntry(name string) bool {
_, removed := s.nameReg.Remove(name)
return removed
}
func (s *State) GetNames() merkle.Tree {
return s.nameReg.Copy()
}
// Set the name reg tree
func (s *State) SetNameReg(nameReg merkle.Tree) {
s.nameReg = nameReg
}
func NameRegEncoder(o interface{}, w io.Writer, n *int64, err *error) {
wire.WriteBinary(o.(*types.NameRegEntry), w, n, err)
}
func NameRegDecoder(r io.Reader, n *int64, err *error) interface{} {
return wire.ReadBinary(&types.NameRegEntry{}, r, n, err)
}
var NameRegCodec = wire.Codec{
Encode: NameRegEncoder,
Decode: NameRegDecoder,
}
// State.nameReg
//-------------------------------------
// Implements events.Eventable. Typically uses events.EventCache
func (s *State) SetFireable(evc events.Fireable) {
s.evc = evc
@ -365,16 +120,16 @@ func (s *State) SetFireable(evc events.Fireable) {
//-----------------------------------------------------------------------------
// Genesis
func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*GenesisDoc, *State) {
func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*types.GenesisDoc, *State) {
jsonBlob, err := ioutil.ReadFile(genDocFile)
if err != nil {
Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
}
genDoc := GenesisDocFromJSON(jsonBlob)
genDoc := types.GenesisDocFromJSON(jsonBlob)
return genDoc, MakeGenesisState(db, genDoc)
}
func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) *State {
if len(genDoc.Validators) == 0 {
Exit(Fmt("The genesis file has no validators"))
}
@ -383,65 +138,14 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
genDoc.GenesisTime = time.Now()
}
// Make accounts state tree
accounts := merkle.NewIAVLTree(wire.BasicCodec, acm.AccountCodec, defaultAccountsCacheCapacity, db)
for _, genAcc := range genDoc.Accounts {
perm := ptypes.ZeroAccountPermissions
if genAcc.Permissions != nil {
perm = *genAcc.Permissions
}
acc := &acm.Account{
Address: genAcc.Address,
PubKey: nil,
Sequence: 0,
Balance: genAcc.Amount,
Permissions: perm,
}
accounts.Set(acc.Address, acc)
}
// global permissions are saved as the 0 address
// so they are included in the accounts tree
globalPerms := ptypes.DefaultAccountPermissions
if genDoc.Params != nil && genDoc.Params.GlobalPermissions != nil {
globalPerms = *genDoc.Params.GlobalPermissions
// XXX: make sure the set bits are all true
// Without it the HasPermission() functions will fail
globalPerms.Base.SetBit = ptypes.AllPermFlags
}
permsAcc := &acm.Account{
Address: ptypes.GlobalPermissionsAddress,
PubKey: nil,
Sequence: 0,
Balance: 1337,
Permissions: globalPerms,
}
accounts.Set(permsAcc.Address, permsAcc)
// XXX Speak to application, ensure genesis state.
// Make validatorInfos state tree && validators slice
validatorInfos := merkle.NewIAVLTree(wire.BasicCodec, types.ValidatorInfoCodec, 0, db)
// Make validators slice
validators := make([]*types.Validator, len(genDoc.Validators))
for i, val := range genDoc.Validators {
pubKey := val.PubKey
address := pubKey.Address()
// Make ValidatorInfo
valInfo := &types.ValidatorInfo{
Address: address,
PubKey: pubKey,
UnbondTo: make([]*types.TxOutput, len(val.UnbondTo)),
FirstBondHeight: 0,
FirstBondAmount: val.Amount,
}
for i, unbondTo := range val.UnbondTo {
valInfo.UnbondTo[i] = &types.TxOutput{
Address: unbondTo.Address,
Amount: unbondTo.Amount,
}
}
validatorInfos.Set(address, valInfo)
// Make validator
validators[i] = &types.Validator{
Address: address,
@ -450,35 +154,22 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
}
}
// Make namereg tree
nameReg := merkle.NewIAVLTree(wire.BasicCodec, NameRegCodec, 0, db)
// TODO: add names, contracts to genesis.json
// IAVLTrees must be persisted before copy operations.
accounts.Save()
validatorInfos.Save()
nameReg.Save()
return &State{
DB: db,
ChainID: genDoc.ChainID,
LastBlockHeight: 0,
LastBlockHash: nil,
LastBlockParts: types.PartSetHeader{},
LastBlockTime: genDoc.GenesisTime,
BondedValidators: types.NewValidatorSet(validators),
LastBondedValidators: types.NewValidatorSet(nil),
UnbondingValidators: types.NewValidatorSet(nil),
accounts: accounts,
validatorInfos: validatorInfos,
nameReg: nameReg,
DB: db,
ChainID: genDoc.ChainID,
LastBlockHeight: 0,
LastBlockHash: nil,
LastBlockParts: types.PartSetHeader{},
LastBlockTime: genDoc.GenesisTime,
Validators: types.NewValidatorSet(validators),
LastValidators: types.NewValidatorSet(nil),
}
}
func RandGenesisState(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*State, []*acm.PrivAccount, []*types.PrivValidator) {
func RandGenesisState(numValidators int, randPower bool, minPower int64) (*State, []*types.PrivValidator) {
db := dbm.NewMemDB()
genDoc, privAccounts, privValidators := RandGenesisDoc(numAccounts, randBalance, minBalance, numValidators, randBonded, minBonded)
genDoc, privValidators := types.RandGenesisDoc(numValidators, randPower, minPower)
s0 := MakeGenesisState(db, genDoc)
s0.Save()
return s0, privAccounts, privValidators
return s0, privValidators
}

+ 0
- 699
state/state_test.go View File

@ -1,699 +0,0 @@
package state
import (
"bytes"
"testing"
"time"
"github.com/tendermint/tendermint/account"
_ "github.com/tendermint/tendermint/config/tendermint_test"
"github.com/tendermint/tendermint/types"
)
func execTxWithState(state *State, tx types.Tx, runCall bool) error {
cache := NewBlockCache(state)
if err := ExecTx(cache, tx, runCall, nil); err != nil {
return err
} else {
cache.Sync()
return nil
}
}
func execTxWithStateNewBlock(state *State, tx types.Tx, runCall bool) error {
if err := execTxWithState(state, tx, runCall); err != nil {
return err
}
state.LastBlockHeight += 1
return nil
}
func TestCopyState(t *testing.T) {
// Generate a random state
s0, privAccounts, _ := RandGenesisState(10, true, 1000, 5, true, 1000)
s0Hash := s0.Hash()
if len(s0Hash) == 0 {
t.Error("Expected state hash")
}
// Check hash of copy
s0Copy := s0.Copy()
if !bytes.Equal(s0Hash, s0Copy.Hash()) {
t.Error("Expected state copy hash to be the same")
}
// Mutate the original; hash should change.
acc0Address := privAccounts[0].PubKey.Address()
acc := s0.GetAccount(acc0Address)
acc.Balance += 1
// The account balance shouldn't have changed yet.
if s0.GetAccount(acc0Address).Balance == acc.Balance {
t.Error("Account balance changed unexpectedly")
}
// Setting, however, should change the balance.
s0.UpdateAccount(acc)
if s0.GetAccount(acc0Address).Balance != acc.Balance {
t.Error("Account balance wasn't set")
}
// Now that the state changed, the hash should change too.
if bytes.Equal(s0Hash, s0.Hash()) {
t.Error("Expected state hash to have changed")
}
// The s0Copy shouldn't have changed though.
if !bytes.Equal(s0Hash, s0Copy.Hash()) {
t.Error("Expected state copy hash to have not changed")
}
}
func makeBlock(t *testing.T, state *State, validation *types.Validation, txs []types.Tx) *types.Block {
if validation == nil {
validation = &types.Validation{}
}
block := &types.Block{
Header: &types.Header{
ChainID: state.ChainID,
Height: state.LastBlockHeight + 1,
Time: state.LastBlockTime.Add(time.Minute),
Fees: 0,
NumTxs: len(txs),
LastBlockHash: state.LastBlockHash,
LastBlockParts: state.LastBlockParts,
StateHash: nil,
},
LastValidation: validation,
Data: &types.Data{
Txs: txs,
},
}
block.FillHeader()
// Fill in block StateHash
err := state.ComputeBlockStateHash(block)
if err != nil {
t.Error("Error appending initial block:", err)
}
if len(block.Header.StateHash) == 0 {
t.Error("Expected StateHash but got nothing.")
}
return block
}
func TestGenesisSaveLoad(t *testing.T) {
// Generate a state, save & load it.
s0, _, _ := RandGenesisState(10, true, 1000, 5, true, 1000)
// Make complete block and blockParts
block := makeBlock(t, s0, nil, nil)
blockParts := block.MakePartSet()
// Now append the block to s0.
err := ExecBlock(s0, block, blockParts.Header())
if err != nil {
t.Error("Error appending initial block:", err)
}
// Save s0
s0.Save()
// Sanity check s0
//s0.DB.(*dbm.MemDB).Print()
if s0.BondedValidators.TotalVotingPower() == 0 {
t.Error("s0 BondedValidators TotalVotingPower should not be 0")
}
if s0.LastBlockHeight != 1 {
t.Error("s0 LastBlockHeight should be 1, got", s0.LastBlockHeight)
}
// Load s1
s1 := LoadState(s0.DB)
// Compare height & blockHash
if s0.LastBlockHeight != s1.LastBlockHeight {
t.Error("LastBlockHeight mismatch")
}
if !bytes.Equal(s0.LastBlockHash, s1.LastBlockHash) {
t.Error("LastBlockHash mismatch")
}
// Compare state merkle trees
if s0.BondedValidators.Size() != s1.BondedValidators.Size() {
t.Error("BondedValidators Size mismatch")
}
if s0.BondedValidators.TotalVotingPower() != s1.BondedValidators.TotalVotingPower() {
t.Error("BondedValidators TotalVotingPower mismatch")
}
if !bytes.Equal(s0.BondedValidators.Hash(), s1.BondedValidators.Hash()) {
t.Error("BondedValidators hash mismatch")
}
if s0.UnbondingValidators.Size() != s1.UnbondingValidators.Size() {
t.Error("UnbondingValidators Size mismatch")
}
if s0.UnbondingValidators.TotalVotingPower() != s1.UnbondingValidators.TotalVotingPower() {
t.Error("UnbondingValidators TotalVotingPower mismatch")
}
if !bytes.Equal(s0.UnbondingValidators.Hash(), s1.UnbondingValidators.Hash()) {
t.Error("UnbondingValidators hash mismatch")
}
if !bytes.Equal(s0.accounts.Hash(), s1.accounts.Hash()) {
t.Error("Accounts mismatch")
}
if !bytes.Equal(s0.validatorInfos.Hash(), s1.validatorInfos.Hash()) {
t.Error("Accounts mismatch")
}
}
func TestTxSequence(t *testing.T) {
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
acc0 := state.GetAccount(privAccounts[0].PubKey.Address())
acc0PubKey := privAccounts[0].PubKey
acc1 := state.GetAccount(privAccounts[1].PubKey.Address())
// Test a variety of sequence numbers for the tx.
// The tx should only pass when i == 1.
for i := -1; i < 3; i++ {
sequence := acc0.Sequence + i
tx := types.NewSendTx()
tx.AddInputWithNonce(acc0PubKey, 1, sequence)
tx.AddOutput(acc1.Address, 1)
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx)
stateCopy := state.Copy()
err := execTxWithState(stateCopy, tx, true)
if i == 1 {
// Sequence is good.
if err != nil {
t.Errorf("Expected good sequence to pass: %v", err)
}
// Check acc.Sequence.
newAcc0 := stateCopy.GetAccount(acc0.Address)
if newAcc0.Sequence != sequence {
t.Errorf("Expected account sequence to change to %v, got %v",
sequence, newAcc0.Sequence)
}
} else {
// Sequence is bad.
if err == nil {
t.Errorf("Expected bad sequence to fail")
}
// Check acc.Sequence. (shouldn't have changed)
newAcc0 := stateCopy.GetAccount(acc0.Address)
if newAcc0.Sequence != acc0.Sequence {
t.Errorf("Expected account sequence to not change from %v, got %v",
acc0.Sequence, newAcc0.Sequence)
}
}
}
}
func TestNameTxs(t *testing.T) {
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
types.MinNameRegistrationPeriod = 5
startingBlock := state.LastBlockHeight
// try some bad names. these should all fail
names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}), "baffledbythespectacleinallofthisyouseeehesaidwithouteyessurprised", "no spaces please"}
data := "something about all this just doesn't feel right."
fee := int64(1000)
numDesiredBlocks := 5
for _, name := range names {
amt := fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data)
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[0])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatalf("Expected invalid name error from %s", name)
}
}
// try some bad data. these should all fail
name := "hold_it_chum"
datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"}
for _, data := range datas {
amt := fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data)
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[0])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatalf("Expected invalid data error from %s", data)
}
}
validateEntry := func(t *testing.T, entry *types.NameRegEntry, name, data string, addr []byte, expires int) {
if entry == nil {
t.Fatalf("Could not find name %s", name)
}
if bytes.Compare(entry.Owner, addr) != 0 {
t.Fatalf("Wrong owner. Got %X expected %X", entry.Owner, addr)
}
if data != entry.Data {
t.Fatalf("Wrong data. Got %s expected %s", entry.Data, data)
}
if name != entry.Name {
t.Fatalf("Wrong name. Got %s expected %s", entry.Name, name)
}
if expires != entry.Expires {
t.Fatalf("Wrong expiry. Got %d, expected %d", entry.Expires, expires)
}
}
// try a good one, check data, owner, expiry
name = "@looking_good/karaoke_bar.broadband"
data = "on this side of neptune there are 1234567890 people: first is OMNIVORE+-3. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')"
amt := fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data)
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[0])
if err := execTxWithState(state, tx, true); err != nil {
t.Fatal(err)
}
entry := state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks)
// fail to update it as non-owner, in same block
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[1])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatal("Expected error")
}
// update it as owner, just to increase expiry, in same block
// NOTE: we have to resend the data or it will clear it (is this what we want?)
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[0])
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*2)
// update it as owner, just to increase expiry, in next block
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[0])
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*3)
// fail to update it as non-owner
state.LastBlockHeight = entry.Expires - 1
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[1])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatal("Expected error")
}
// once expires, non-owner succeeds
state.LastBlockHeight = entry.Expires
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[1])
if err := execTxWithState(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[1].Address, state.LastBlockHeight+numDesiredBlocks)
// update it as new owner, with new data (longer), but keep the expiry!
data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that."
oldCredit := amt - fee
numDesiredBlocks = 10
amt = fee + (int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) - oldCredit)
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[1])
if err := execTxWithState(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[1].Address, state.LastBlockHeight+numDesiredBlocks)
// test removal
amt = fee
data = ""
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[1])
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
if entry != nil {
t.Fatal("Expected removed entry to be nil")
}
// create entry by key0,
// test removal by key1 after expiry
name = "looking_good/karaoke_bar"
data = "some data"
amt = fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data)
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[0])
if err := execTxWithState(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[0].Address, state.LastBlockHeight+numDesiredBlocks)
state.LastBlockHeight = entry.Expires
amt = fee
data = ""
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(state.ChainID, privAccounts[1])
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
if entry != nil {
t.Fatal("Expected removed entry to be nil")
}
}
// TODO: test overflows.
// TODO: test for unbonding validators.
func TestTxs(t *testing.T) {
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
//val0 := state.GetValidatorInfo(privValidators[0].Address)
acc0 := state.GetAccount(privAccounts[0].PubKey.Address())
acc0PubKey := privAccounts[0].PubKey
acc1 := state.GetAccount(privAccounts[1].PubKey.Address())
// SendTx.
{
state := state.Copy()
tx := &types.SendTx{
Inputs: []*types.TxInput{
&types.TxInput{
Address: acc0.Address,
Amount: 1,
Sequence: acc0.Sequence + 1,
PubKey: acc0PubKey,
},
},
Outputs: []*types.TxOutput{
&types.TxOutput{
Address: acc1.Address,
Amount: 1,
},
},
}
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx)
err := execTxWithState(state, tx, true)
if err != nil {
t.Errorf("Got error in executing send transaction, %v", err)
}
newAcc0 := state.GetAccount(acc0.Address)
if acc0.Balance-1 != newAcc0.Balance {
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
acc0.Balance-1, newAcc0.Balance)
}
newAcc1 := state.GetAccount(acc1.Address)
if acc1.Balance+1 != newAcc1.Balance {
t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v",
acc1.Balance+1, newAcc1.Balance)
}
}
// CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more
{
state := state.Copy()
newAcc1 := state.GetAccount(acc1.Address)
newAcc1.Code = []byte{0x60}
state.UpdateAccount(newAcc1)
tx := &types.CallTx{
Input: &types.TxInput{
Address: acc0.Address,
Amount: 1,
Sequence: acc0.Sequence + 1,
PubKey: acc0PubKey,
},
Address: acc1.Address,
GasLimit: 10,
}
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
err := execTxWithState(state, tx, true)
if err != nil {
t.Errorf("Got error in executing call transaction, %v", err)
}
newAcc0 := state.GetAccount(acc0.Address)
if acc0.Balance-1 != newAcc0.Balance {
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
acc0.Balance-1, newAcc0.Balance)
}
newAcc1 = state.GetAccount(acc1.Address)
if acc1.Balance+1 != newAcc1.Balance {
t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v",
acc1.Balance+1, newAcc1.Balance)
}
}
// NameTx.
{
entryName := "satoshi"
entryData := `
A purely peer-to-peer version of electronic cash would allow online
payments to be sent directly from one party to another without going through a
financial institution. Digital signatures provide part of the solution, but the main
benefits are lost if a trusted third party is still required to prevent double-spending.
We propose a solution to the double-spending problem using a peer-to-peer network.
The network timestamps transactions by hashing them into an ongoing chain of
hash-based proof-of-work, forming a record that cannot be changed without redoing
the proof-of-work. The longest chain not only serves as proof of the sequence of
events witnessed, but proof that it came from the largest pool of CPU power. As
long as a majority of CPU power is controlled by nodes that are not cooperating to
attack the network, they'll generate the longest chain and outpace attackers. The
network itself requires minimal structure. Messages are broadcast on a best effort
basis, and nodes can leave and rejoin the network at will, accepting the longest
proof-of-work chain as proof of what happened while they were gone `
entryAmount := int64(10000)
state := state.Copy()
tx := &types.NameTx{
Input: &types.TxInput{
Address: acc0.Address,
Amount: entryAmount,
Sequence: acc0.Sequence + 1,
PubKey: acc0PubKey,
},
Name: entryName,
Data: entryData,
}
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
err := execTxWithState(state, tx, true)
if err != nil {
t.Errorf("Got error in executing call transaction, %v", err)
}
newAcc0 := state.GetAccount(acc0.Address)
if acc0.Balance-entryAmount != newAcc0.Balance {
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
acc0.Balance-entryAmount, newAcc0.Balance)
}
entry := state.GetNameRegEntry(entryName)
if entry == nil {
t.Errorf("Expected an entry but got nil")
}
if entry.Data != entryData {
t.Errorf("Wrong data stored")
}
// test a bad string
tx.Data = string([]byte{0, 1, 2, 3, 127, 128, 129, 200, 251})
tx.Input.Sequence += 1
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
err = execTxWithState(state, tx, true)
if _, ok := err.(types.ErrTxInvalidString); !ok {
t.Errorf("Expected invalid string error. Got: %s", err.Error())
}
}
// BondTx.
{
state := state.Copy()
tx := &types.BondTx{
PubKey: acc0PubKey.(account.PubKeyEd25519),
Inputs: []*types.TxInput{
&types.TxInput{
Address: acc0.Address,
Amount: 1,
Sequence: acc0.Sequence + 1,
PubKey: acc0PubKey,
},
},
UnbondTo: []*types.TxOutput{
&types.TxOutput{
Address: acc0.Address,
Amount: 1,
},
},
}
tx.Signature = privAccounts[0].Sign(state.ChainID, tx).(account.SignatureEd25519)
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx)
err := execTxWithState(state, tx, true)
if err != nil {
t.Errorf("Got error in executing bond transaction, %v", err)
}
newAcc0 := state.GetAccount(acc0.Address)
if newAcc0.Balance != acc0.Balance-1 {
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
acc0.Balance-1, newAcc0.Balance)
}
_, acc0Val := state.BondedValidators.GetByAddress(acc0.Address)
if acc0Val == nil {
t.Errorf("acc0Val not present")
}
if acc0Val.BondHeight != state.LastBlockHeight+1 {
t.Errorf("Unexpected bond height. Expected %v, got %v",
state.LastBlockHeight, acc0Val.BondHeight)
}
if acc0Val.VotingPower != 1 {
t.Errorf("Unexpected voting power. Expected %v, got %v",
acc0Val.VotingPower, acc0.Balance)
}
if acc0Val.Accum != 0 {
t.Errorf("Unexpected accum. Expected 0, got %v",
acc0Val.Accum)
}
}
// TODO UnbondTx.
}
func TestSuicide(t *testing.T) {
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
acc0 := state.GetAccount(privAccounts[0].PubKey.Address())
acc0PubKey := privAccounts[0].PubKey
acc1 := state.GetAccount(privAccounts[1].PubKey.Address())
acc2 := state.GetAccount(privAccounts[2].Address)
sendingAmount, refundedBalance, oldBalance := int64(1), acc1.Balance, acc2.Balance
newAcc1 := state.GetAccount(acc1.Address)
// store 0x1 at 0x1, push an address, then suicide :)
contractCode := []byte{0x60, 0x01, 0x60, 0x01, 0x55, 0x73}
contractCode = append(contractCode, acc2.Address...)
contractCode = append(contractCode, 0xff)
newAcc1.Code = contractCode
state.UpdateAccount(newAcc1)
// send call tx with no data, cause suicide
tx := types.NewCallTxWithNonce(acc0PubKey, acc1.Address, nil, sendingAmount, 1000, 0, acc0.Sequence+1)
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
// we use cache instead of execTxWithState so we can run the tx twice
cache := NewBlockCache(state)
if err := ExecTx(cache, tx, true, nil); err != nil {
t.Errorf("Got error in executing call transaction, %v", err)
}
// if we do it again, we won't get an error, but the suicide
// shouldn't happen twice and the caller should lose fee
tx.Input.Sequence += 1
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
if err := ExecTx(cache, tx, true, nil); err != nil {
t.Errorf("Got error in executing call transaction, %v", err)
}
// commit the block
cache.Sync()
// acc2 should receive the sent funds and the contracts balance
newAcc2 := state.GetAccount(acc2.Address)
newBalance := sendingAmount + refundedBalance + oldBalance
if newAcc2.Balance != newBalance {
t.Errorf("Unexpected newAcc2 balance. Expected %v, got %v",
newAcc2.Balance, newBalance)
}
newAcc1 = state.GetAccount(acc1.Address)
if newAcc1 != nil {
t.Errorf("Expected account to be removed")
}
}
func TestAddValidator(t *testing.T) {
// Generate a state, save & load it.
s0, privAccounts, privValidators := RandGenesisState(10, false, 1000, 1, false, 1000)
// The first privAccount will become a validator
acc0 := privAccounts[0]
bondTx := &types.BondTx{
PubKey: acc0.PubKey.(account.PubKeyEd25519),
Inputs: []*types.TxInput{
&types.TxInput{
Address: acc0.Address,
Amount: 1000,
Sequence: 1,
PubKey: acc0.PubKey,
},
},
UnbondTo: []*types.TxOutput{
&types.TxOutput{
Address: acc0.Address,
Amount: 1000,
},
},
}
bondTx.Signature = acc0.Sign(s0.ChainID, bondTx).(account.SignatureEd25519)
bondTx.Inputs[0].Signature = acc0.Sign(s0.ChainID, bondTx)
// Make complete block and blockParts
block0 := makeBlock(t, s0, nil, []types.Tx{bondTx})
block0Parts := block0.MakePartSet()
// Sanity check
if s0.BondedValidators.Size() != 1 {
t.Error("Expected there to be 1 validators before bondTx")
}
// Now append the block to s0.
err := ExecBlock(s0, block0, block0Parts.Header())
if err != nil {
t.Error("Error appending initial block:", err)
}
// Must save before further modification
s0.Save()
// Test new validator set
if s0.BondedValidators.Size() != 2 {
t.Error("Expected there to be 2 validators after bondTx")
}
// The validation for the next block should only require 1 signature
// (the new validator wasn't active for block0)
precommit0 := &types.Vote{
Height: 1,
Round: 0,
Type: types.VoteTypePrecommit,
BlockHash: block0.Hash(),
BlockPartsHeader: block0Parts.Header(),
}
privValidators[0].SignVote(s0.ChainID, precommit0)
block1 := makeBlock(t, s0,
&types.Validation{
Precommits: []*types.Vote{
precommit0,
},
}, nil,
)
block1Parts := block1.MakePartSet()
err = ExecBlock(s0, block1, block1Parts.Header())
if err != nil {
t.Error("Error appending secondary block:", err)
}
}

+ 0
- 198
state/tx_cache.go View File

@ -1,198 +0,0 @@
package state
import (
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
ptypes "github.com/tendermint/tendermint/permission/types" // for GlobalPermissionAddress ...
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/vm"
)
type TxCache struct {
backend *BlockCache
accounts map[Word256]vmAccountInfo
storages map[Tuple256]Word256
}
func NewTxCache(backend *BlockCache) *TxCache {
return &TxCache{
backend: backend,
accounts: make(map[Word256]vmAccountInfo),
storages: make(map[Tuple256]Word256),
}
}
//-------------------------------------
// TxCache.account
func (cache *TxCache) GetAccount(addr Word256) *vm.Account {
acc, removed := cache.accounts[addr].unpack()
if removed {
return nil
} else if acc == nil {
acc2 := cache.backend.GetAccount(addr.Postfix(20))
if acc2 != nil {
return toVMAccount(acc2)
}
}
return acc
}
func (cache *TxCache) UpdateAccount(acc *vm.Account) {
addr := acc.Address
_, removed := cache.accounts[addr].unpack()
if removed {
PanicSanity("UpdateAccount on a removed account")
}
cache.accounts[addr] = vmAccountInfo{acc, false}
}
func (cache *TxCache) RemoveAccount(acc *vm.Account) {
addr := acc.Address
_, removed := cache.accounts[addr].unpack()
if removed {
PanicSanity("RemoveAccount on a removed account")
}
cache.accounts[addr] = vmAccountInfo{acc, true}
}
// Creates a 20 byte address and bumps the creator's nonce.
func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account {
// Generate an address
nonce := creator.Nonce
creator.Nonce += 1
addr := LeftPadWord256(NewContractAddress(creator.Address.Postfix(20), int(nonce)))
// Create account from address.
account, removed := cache.accounts[addr].unpack()
if removed || account == nil {
account = &vm.Account{
Address: addr,
Balance: 0,
Code: nil,
Nonce: 0,
Permissions: cache.GetAccount(ptypes.GlobalPermissionsAddress256).Permissions,
Other: vmAccountOther{
PubKey: nil,
StorageRoot: nil,
},
}
cache.accounts[addr] = vmAccountInfo{account, false}
return account
} else {
// either we've messed up nonce handling, or sha3 is broken
PanicSanity(Fmt("Could not create account, address already exists: %X", addr))
return nil
}
}
// TxCache.account
//-------------------------------------
// TxCache.storage
func (cache *TxCache) GetStorage(addr Word256, key Word256) Word256 {
// Check cache
value, ok := cache.storages[Tuple256{addr, key}]
if ok {
return value
}
// Load from backend
return cache.backend.GetStorage(addr, key)
}
// NOTE: Set value to zero to removed from the trie.
func (cache *TxCache) SetStorage(addr Word256, key Word256, value Word256) {
_, removed := cache.accounts[addr].unpack()
if removed {
PanicSanity("SetStorage() on a removed account")
}
cache.storages[Tuple256{addr, key}] = value
}
// TxCache.storage
//-------------------------------------
// These updates do not have to be in deterministic order,
// the backend is responsible for ordering updates.
func (cache *TxCache) Sync() {
// Remove or update storage
for addrKey, value := range cache.storages {
addr, key := Tuple256Split(addrKey)
cache.backend.SetStorage(addr, key, value)
}
// Remove or update accounts
for addr, accInfo := range cache.accounts {
acc, removed := accInfo.unpack()
if removed {
cache.backend.RemoveAccount(addr.Postfix(20))
} else {
cache.backend.UpdateAccount(toStateAccount(acc))
}
}
}
//-----------------------------------------------------------------------------
// Convenience function to return address of new contract
func NewContractAddress(caller []byte, nonce int) []byte {
return types.NewContractAddress(caller, nonce)
}
// Converts backend.Account to vm.Account struct.
func toVMAccount(acc *acm.Account) *vm.Account {
return &vm.Account{
Address: LeftPadWord256(acc.Address),
Balance: acc.Balance,
Code: acc.Code, // This is crazy.
Nonce: int64(acc.Sequence),
Permissions: acc.Permissions, // Copy
Other: vmAccountOther{
PubKey: acc.PubKey,
StorageRoot: acc.StorageRoot,
},
}
}
// Converts vm.Account to backend.Account struct.
func toStateAccount(acc *vm.Account) *acm.Account {
var pubKey acm.PubKey
var storageRoot []byte
if acc.Other != nil {
pubKey, storageRoot = acc.Other.(vmAccountOther).unpack()
}
return &acm.Account{
Address: acc.Address.Postfix(20),
PubKey: pubKey,
Balance: acc.Balance,
Code: acc.Code,
Sequence: int(acc.Nonce),
StorageRoot: storageRoot,
Permissions: acc.Permissions, // Copy
}
}
// Everything in acmAccount that doesn't belong in
// exported vmAccount fields.
type vmAccountOther struct {
PubKey acm.PubKey
StorageRoot []byte
}
func (accOther vmAccountOther) unpack() (acm.PubKey, []byte) {
return accOther.PubKey, accOther.StorageRoot
}
type vmAccountInfo struct {
account *vm.Account
removed bool
}
func (accInfo vmAccountInfo) unpack() (*vm.Account, bool) {
return accInfo.account, accInfo.removed
}

+ 0
- 23
state/tx_cache_test.go View File

@ -1,23 +0,0 @@
package state
import (
"bytes"
"testing"
stypes "github.com/tendermint/tendermint/state/types"
"github.com/tendermint/go-wire"
)
func TestStateToFromVMAccount(t *testing.T) {
acmAcc1, _ := stypes.RandAccount(true, 456)
vmAcc := toVMAccount(acmAcc1)
acmAcc2 := toStateAccount(vmAcc)
acmAcc1Bytes := wire.BinaryBytes(acmAcc1)
acmAcc2Bytes := wire.BinaryBytes(acmAcc2)
if !bytes.Equal(acmAcc1Bytes, acmAcc2Bytes) {
t.Errorf("Unexpected account wire bytes\n%X vs\n%X",
acmAcc1Bytes, acmAcc2Bytes)
}
}

+ 0
- 121
state/types/genesis.go View File

@ -1,121 +0,0 @@
package types
import (
"sort"
"time"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
ptypes "github.com/tendermint/tendermint/permission/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/go-wire"
)
//------------------------------------------------------------
// we store the gendoc in the db
var GenDocKey = []byte("GenDocKey")
//------------------------------------------------------------
// core types for a genesis definition
type BasicAccount struct {
Address []byte `json:"address"`
Amount int64 `json:"amount"`
}
type GenesisAccount struct {
Address []byte `json:"address"`
Amount int64 `json:"amount"`
Name string `json:"name"`
Permissions *ptypes.AccountPermissions `json:"permissions"`
}
type GenesisValidator struct {
PubKey acm.PubKeyEd25519 `json:"pub_key"`
Amount int64 `json:"amount"`
Name string `json:"name"`
UnbondTo []BasicAccount `json:"unbond_to"`
}
type GenesisParams struct {
GlobalPermissions *ptypes.AccountPermissions `json:"global_permissions"`
}
type GenesisDoc struct {
GenesisTime time.Time `json:"genesis_time"`
ChainID string `json:"chain_id"`
Params *GenesisParams `json:"params"`
Accounts []GenesisAccount `json:"accounts"`
Validators []GenesisValidator `json:"validators"`
}
//------------------------------------------------------------
// Make genesis state from file
func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) {
var err error
wire.ReadJSONPtr(&genState, jsonBlob, &err)
if err != nil {
Exit(Fmt("Couldn't read GenesisDoc: %v", err))
}
return
}
//------------------------------------------------------------
// Make random genesis state
func RandAccount(randBalance bool, minBalance int64) (*acm.Account, *acm.PrivAccount) {
privAccount := acm.GenPrivAccount()
perms := ptypes.DefaultAccountPermissions
acc := &acm.Account{
Address: privAccount.PubKey.Address(),
PubKey: privAccount.PubKey,
Sequence: RandInt(),
Balance: minBalance,
Permissions: perms,
}
if randBalance {
acc.Balance += int64(RandUint32())
}
return acc, privAccount
}
func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) {
accounts := make([]GenesisAccount, numAccounts)
privAccounts := make([]*acm.PrivAccount, numAccounts)
defaultPerms := ptypes.DefaultAccountPermissions
for i := 0; i < numAccounts; i++ {
account, privAccount := RandAccount(randBalance, minBalance)
accounts[i] = GenesisAccount{
Address: account.Address,
Amount: account.Balance,
Permissions: &defaultPerms, // This will get copied into each state.Account.
}
privAccounts[i] = privAccount
}
validators := make([]GenesisValidator, numValidators)
privValidators := make([]*types.PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
valInfo, _, privVal := types.RandValidator(randBonded, minBonded)
validators[i] = GenesisValidator{
PubKey: valInfo.PubKey,
Amount: valInfo.FirstBondAmount,
UnbondTo: []BasicAccount{
{
Address: valInfo.PubKey.Address(),
Amount: valInfo.FirstBondAmount,
},
},
}
privValidators[i] = privVal
}
sort.Sort(types.PrivValidatorsByAddress(privValidators))
return &GenesisDoc{
GenesisTime: time.Now(),
ChainID: "tendermint_test",
Accounts: accounts,
Validators: validators,
}, privAccounts, privValidators
}

+ 3
- 4
types/block.go View File

@ -7,7 +7,6 @@ import (
"strings"
"time"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-merkle"
"github.com/tendermint/go-wire"
@ -322,11 +321,11 @@ type Data struct {
func (data *Data) Hash() []byte {
if data.hash == nil {
bs := make([]interface{}, len(data.Txs))
txs := make([]interface{}, len(data.Txs))
for i, tx := range data.Txs {
bs[i] = acm.SignBytes(config.GetString("chain_id"), tx)
txs[i] = tx
}
data.hash = merkle.SimpleHashFromBinaries(bs) // NOTE: leaves are TxIDs.
data.hash = merkle.SimpleHashFromBinaries(txs) // NOTE: leaves are TxIDs.
}
return data.hash
}


+ 65
- 0
types/genesis.go View File

@ -0,0 +1,65 @@
package types
import (
"sort"
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
)
//------------------------------------------------------------
// we store the gendoc in the db
var GenDocKey = []byte("GenDocKey")
//------------------------------------------------------------
// core types for a genesis definition
type GenesisValidator struct {
PubKey crypto.PubKeyEd25519 `json:"pub_key"`
Amount int64 `json:"amount"`
Name string `json:"name"`
}
type GenesisDoc struct {
GenesisTime time.Time `json:"genesis_time"`
ChainID string `json:"chain_id"`
Validators []GenesisValidator `json:"validators"`
}
//------------------------------------------------------------
// Make genesis state from file
func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) {
var err error
wire.ReadJSONPtr(&genState, jsonBlob, &err)
if err != nil {
Exit(Fmt("Couldn't read GenesisDoc: %v", err))
}
return
}
//------------------------------------------------------------
// Make random genesis state
func RandGenesisDoc(numValidators int, randPower bool, minPower int64) (*GenesisDoc, []*PrivValidator) {
validators := make([]GenesisValidator, numValidators)
privValidators := make([]*PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
val, privVal := RandValidator(randPower, minPower)
validators[i] = GenesisValidator{
PubKey: val.PubKey,
Amount: val.VotingPower,
}
privValidators[i] = privVal
}
sort.Sort(PrivValidatorsByAddress(privValidators))
return &GenesisDoc{
GenesisTime: time.Now(),
ChainID: "tendermint_test",
Validators: validators,
}, privValidators
}

+ 0
- 55
types/names.go View File

@ -1,55 +0,0 @@
package types
import (
"regexp"
)
var (
MinNameRegistrationPeriod int = 5
// NOTE: base costs and validity checks are here so clients
// can use them without importing state
// cost for storing a name for a block is
// CostPerBlock*CostPerByte*(len(data) + 32)
NameByteCostMultiplier int64 = 1
NameBlockCostMultiplier int64 = 1
MaxNameLength = 64
MaxDataLength = 1 << 16
// Name should be file system lik
// Data should be anything permitted in JSON
regexpAlphaNum = regexp.MustCompile("^[a-zA-Z0-9._/-@]*$")
regexpJSON = regexp.MustCompile(`^[a-zA-Z0-9_/ \-+"':,\n\t.{}()\[\]]*$`)
)
// filter strings
func validateNameRegEntryName(name string) bool {
return regexpAlphaNum.Match([]byte(name))
}
func validateNameRegEntryData(data string) bool {
return regexpJSON.Match([]byte(data))
}
// base cost is "effective" number of bytes
func NameBaseCost(name, data string) int64 {
return int64(len(data) + 32)
}
func NameCostPerBlock(baseCost int64) int64 {
return NameBlockCostMultiplier * NameByteCostMultiplier * baseCost
}
type NameRegEntry struct {
Name string `json:"name"` // registered name for the entry
Owner []byte `json:"owner"` // address that created the entry
Data string `json:"data"` // data to store under this name
Expires int `json:"expires"` // block at which this entry expires
}
func (entry *NameRegEntry) Copy() *NameRegEntry {
entryCopy := *entry
return &entryCopy
}

+ 0
- 67
types/node.go View File

@ -1,67 +0,0 @@
package types
import (
"fmt"
acm "github.com/tendermint/tendermint/account"
"strings"
)
type NodeInfo struct {
PubKey acm.PubKeyEd25519 `json:"pub_key"`
Moniker string `json:"moniker"`
ChainID string `json:"chain_id"`
Host string `json:"host"`
P2PPort uint16 `json:"p2p_port"`
RPCPort uint16 `json:"rpc_port"`
Version Versions `json:"versions"`
}
type Versions struct {
Revision string `json:"revision"`
Tendermint string `json:"tendermint"`
P2P string `json:"p2p"`
RPC string `json:"rpc"`
Wire string `json:"wire"`
}
// CONTRACT: two nodes with the same Tendermint major and minor version and with the same ChainID are compatible
func (ni *NodeInfo) CompatibleWith(no *NodeInfo) error {
iM, im, _, ie := splitVersion(ni.Version.Tendermint)
oM, om, _, oe := splitVersion(no.Version.Tendermint)
// if our own version number is not formatted right, we messed up
if ie != nil {
return ie
}
// version number must be formatted correctly ("x.x.x")
if oe != nil {
return oe
}
// major version must match
if iM != oM {
return fmt.Errorf("Peer is on a different major version. Got %v, expected %v", oM, iM)
}
// minor version must match
if im != om {
return fmt.Errorf("Peer is on a different minor version. Got %v, expected %v", om, im)
}
// nodes must be on the same chain_id
if ni.ChainID != no.ChainID {
return fmt.Errorf("Peer is on a different chain_id. Got %v, expected %v", no.ChainID, ni.ChainID)
}
return nil
}
func splitVersion(version string) (string, string, string, error) {
spl := strings.Split(version, ".")
if len(spl) != 3 {
return "", "", "", fmt.Errorf("Invalid version format %v", version)
}
return spl[0], spl[1], spl[2], nil
}

+ 11
- 33
types/priv_validator.go View File

@ -1,4 +1,3 @@
package types
import (
@ -6,12 +5,11 @@ import (
"errors"
"fmt"
"io/ioutil"
"math"
"os"
"sync"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
"github.com/tendermint/ed25519"
@ -37,12 +35,12 @@ func voteToStep(vote *Vote) int8 {
}
type PrivValidator struct {
Address []byte `json:"address"`
PubKey acm.PubKeyEd25519 `json:"pub_key"`
PrivKey acm.PrivKeyEd25519 `json:"priv_key"`
LastHeight int `json:"last_height"`
LastRound int `json:"last_round"`
LastStep int8 `json:"last_step"`
Address []byte `json:"address"`
PubKey crypto.PubKeyEd25519 `json:"pub_key"`
PrivKey crypto.PrivKeyEd25519 `json:"priv_key"`
LastHeight int `json:"last_height"`
LastRound int `json:"last_round"`
LastStep int8 `json:"last_step"`
// For persistence.
// Overloaded for testing.
@ -55,8 +53,8 @@ func GenPrivValidator() *PrivValidator {
privKeyBytes := new([64]byte)
copy(privKeyBytes[:32], CRandBytes(32))
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes)
pubKey := acm.PubKeyEd25519(*pubKeyBytes)
privKey := acm.PrivKeyEd25519(*privKeyBytes)
pubKey := crypto.PubKeyEd25519(*pubKeyBytes)
privKey := crypto.PrivKeyEd25519(*privKeyBytes)
return &PrivValidator{
Address: pubKey.Address(),
PubKey: pubKey,
@ -152,7 +150,7 @@ func (privVal *PrivValidator) SignVote(chainID string, vote *Vote) error {
}
func (privVal *PrivValidator) SignVoteUnsafe(chainID string, vote *Vote) {
vote.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, vote)).(acm.SignatureEd25519)
vote.Signature = privVal.PrivKey.Sign(SignBytes(chainID, vote)).(crypto.SignatureEd25519)
}
func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error {
@ -169,33 +167,13 @@ func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) e
privVal.save()
// Sign
proposal.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, proposal)).(acm.SignatureEd25519)
proposal.Signature = privVal.PrivKey.Sign(SignBytes(chainID, proposal)).(crypto.SignatureEd25519)
return nil
} else {
return errors.New(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v", proposal.Height, proposal.Round))
}
}
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))
}
}
func (privVal *PrivValidator) String() string {
return fmt.Sprintf("PrivValidator{%X LH:%v, LR:%v, LS:%v}", privVal.Address, privVal.LastHeight, privVal.LastRound, privVal.LastStep)
}


+ 6
- 6
types/proposal.go View File

@ -5,8 +5,8 @@ import (
"fmt"
"io"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
)
@ -16,11 +16,11 @@ var (
)
type Proposal struct {
Height int `json:"height"`
Round int `json:"round"`
BlockPartsHeader PartSetHeader `json:"block_parts_header"`
POLRound int `json:"pol_round"` // -1 if null.
Signature acm.SignatureEd25519 `json:"signature"`
Height int `json:"height"`
Round int `json:"round"`
BlockPartsHeader PartSetHeader `json:"block_parts_header"`
POLRound int `json:"pol_round"` // -1 if null.
Signature crypto.SignatureEd25519 `json:"signature"`
}
func NewProposal(height int, round int, blockPartsHeader PartSetHeader, polRound int) *Proposal {


+ 1
- 2
types/proposal_test.go View File

@ -3,7 +3,6 @@ package types
import (
"testing"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
_ "github.com/tendermint/tendermint/config/tendermint_test"
)
@ -15,7 +14,7 @@ func TestProposalSignable(t *testing.T) {
BlockPartsHeader: PartSetHeader{111, []byte("blockparts")},
POLRound: -1,
}
signBytes := acm.SignBytes(config.GetString("chain_id"), proposal)
signBytes := 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}}`,


+ 30
- 0
types/signable.go View File

@ -0,0 +1,30 @@
package types
import (
"bytes"
"io"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-merkle"
)
// Signable is an interface for all signable things.
// It typically removes signatures before serializing.
type Signable interface {
WriteSignBytes(chainID string, w io.Writer, n *int64, err *error)
}
// SignBytes is a convenience method for getting the bytes to sign of a Signable.
func SignBytes(chainID string, o Signable) []byte {
buf, n, err := new(bytes.Buffer), new(int64), new(error)
o.WriteSignBytes(chainID, buf, n, err)
if *err != nil {
PanicCrisis(err)
}
return buf.Bytes()
}
// HashSignBytes is a convenience method for getting the hash of the bytes of a signable
func HashSignBytes(chainID string, o Signable) []byte {
return merkle.SimpleHashFromBinary(SignBytes(chainID, o))
}

+ 1
- 373
types/tx.go View File

@ -1,375 +1,3 @@
package types
import (
"encoding/json"
"errors"
"io"
"golang.org/x/crypto/ripemd160"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
ptypes "github.com/tendermint/tendermint/permission/types"
"github.com/tendermint/go-wire"
)
var (
ErrTxInvalidAddress = errors.New("Error invalid address")
ErrTxDuplicateAddress = errors.New("Error duplicate address")
ErrTxInvalidAmount = errors.New("Error invalid amount")
ErrTxInsufficientFunds = errors.New("Error insufficient funds")
ErrTxInsufficientGasPrice = errors.New("Error insufficient gas price")
ErrTxUnknownPubKey = errors.New("Error unknown pubkey")
ErrTxInvalidPubKey = errors.New("Error invalid pubkey")
ErrTxInvalidSignature = errors.New("Error invalid signature")
ErrTxPermissionDenied = errors.New("Error permission denied")
)
type ErrTxInvalidString struct {
Msg string
}
func (e ErrTxInvalidString) Error() string {
return e.Msg
}
type ErrTxInvalidSequence struct {
Got int
Expected int
}
func (e ErrTxInvalidSequence) Error() string {
return Fmt("Error invalid sequence. Got %d, expected %d", e.Got, e.Expected)
}
/*
Tx (Transaction) is an atomic operation on the ledger state.
Account Txs:
- SendTx Send coins to address
- CallTx Send a msg to a contract that runs in the vm
- NameTx Store some value under a name in the global namereg
Validation Txs:
- BondTx New validator posts a bond
- UnbondTx Validator leaves
- DupeoutTx Validator dupes out (equivocates)
Admin Txs:
- PermissionsTx
*/
type Tx interface {
WriteSignBytes(chainID string, w io.Writer, n *int64, err *error)
}
// Types of Tx implementations
const (
// Account transactions
TxTypeSend = byte(0x01)
TxTypeCall = byte(0x02)
TxTypeName = byte(0x03)
// Validation transactions
TxTypeBond = byte(0x11)
TxTypeUnbond = byte(0x12)
TxTypeRebond = byte(0x13)
TxTypeDupeout = byte(0x14)
// Admin transactions
TxTypePermissions = byte(0x20)
)
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ Tx }{},
wire.ConcreteType{&SendTx{}, TxTypeSend},
wire.ConcreteType{&CallTx{}, TxTypeCall},
wire.ConcreteType{&NameTx{}, TxTypeName},
wire.ConcreteType{&BondTx{}, TxTypeBond},
wire.ConcreteType{&UnbondTx{}, TxTypeUnbond},
wire.ConcreteType{&RebondTx{}, TxTypeRebond},
wire.ConcreteType{&DupeoutTx{}, TxTypeDupeout},
wire.ConcreteType{&PermissionsTx{}, TxTypePermissions},
)
//-----------------------------------------------------------------------------
type TxInput struct {
Address []byte `json:"address"` // Hash of the PubKey
Amount int64 `json:"amount"` // Must not exceed account balance
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
Signature acm.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
PubKey acm.PubKey `json:"pub_key"` // Must not be nil, may be nil
}
func (txIn *TxInput) ValidateBasic() error {
if len(txIn.Address) != 20 {
return ErrTxInvalidAddress
}
if txIn.Amount == 0 {
return ErrTxInvalidAmount
}
return nil
}
func (txIn *TxInput) WriteSignBytes(w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"address":"%X","amount":%v,"sequence":%v}`, txIn.Address, txIn.Amount, txIn.Sequence)), w, n, err)
}
func (txIn *TxInput) String() string {
return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PubKey)
}
//-----------------------------------------------------------------------------
type TxOutput struct {
Address []byte `json:"address"` // Hash of the PubKey
Amount int64 `json:"amount"` // The sum of all outputs must not exceed the inputs.
}
func (txOut *TxOutput) ValidateBasic() error {
if len(txOut.Address) != 20 {
return ErrTxInvalidAddress
}
if txOut.Amount == 0 {
return ErrTxInvalidAmount
}
return nil
}
func (txOut *TxOutput) WriteSignBytes(w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"address":"%X","amount":%v}`, txOut.Address, txOut.Amount)), w, n, err)
}
func (txOut *TxOutput) String() string {
return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount)
}
//-----------------------------------------------------------------------------
type SendTx struct {
Inputs []*TxInput `json:"inputs"`
Outputs []*TxOutput `json:"outputs"`
}
func (tx *SendTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend)), w, n, err)
for i, in := range tx.Inputs {
in.WriteSignBytes(w, n, err)
if i != len(tx.Inputs)-1 {
wire.WriteTo([]byte(","), w, n, err)
}
}
wire.WriteTo([]byte(`],"outputs":[`), w, n, err)
for i, out := range tx.Outputs {
out.WriteSignBytes(w, n, err)
if i != len(tx.Outputs)-1 {
wire.WriteTo([]byte(","), w, n, err)
}
}
wire.WriteTo([]byte(`]}]}`), w, n, err)
}
func (tx *SendTx) String() string {
return Fmt("SendTx{%v -> %v}", tx.Inputs, tx.Outputs)
}
//-----------------------------------------------------------------------------
type CallTx struct {
Input *TxInput `json:"input"`
Address []byte `json:"address"`
GasLimit int64 `json:"gas_limit"`
Fee int64 `json:"fee"`
Data []byte `json:"data"`
}
func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data)), w, n, err)
wire.WriteTo([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)), w, n, err)
tx.Input.WriteSignBytes(w, n, err)
wire.WriteTo([]byte(`}]}`), w, n, err)
}
func (tx *CallTx) String() string {
return Fmt("CallTx{%v -> %x: %x}", tx.Input, tx.Address, tx.Data)
}
func NewContractAddress(caller []byte, nonce int) []byte {
temp := make([]byte, 32+8)
copy(temp, caller)
PutInt64BE(temp[32:], int64(nonce))
hasher := ripemd160.New()
hasher.Write(temp) // does not error
return hasher.Sum(nil)
}
//-----------------------------------------------------------------------------
type NameTx struct {
Input *TxInput `json:"input"`
Name string `json:"name"`
Data string `json:"data"`
Fee int64 `json:"fee"`
}
func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"data":%s,"fee":%v`, TxTypeName, jsonEscape(tx.Data), tx.Fee)), w, n, err)
wire.WriteTo([]byte(`,"input":`), w, n, err)
tx.Input.WriteSignBytes(w, n, err)
wire.WriteTo([]byte(Fmt(`,"name":%s`, jsonEscape(tx.Name))), w, n, err)
wire.WriteTo([]byte(`}]}`), w, n, err)
}
func (tx *NameTx) ValidateStrings() error {
if len(tx.Name) == 0 {
return ErrTxInvalidString{"Name must not be empty"}
}
if len(tx.Name) > MaxNameLength {
return ErrTxInvalidString{Fmt("Name is too long. Max %d bytes", MaxNameLength)}
}
if len(tx.Data) > MaxDataLength {
return ErrTxInvalidString{Fmt("Data is too long. Max %d bytes", MaxDataLength)}
}
if !validateNameRegEntryName(tx.Name) {
return ErrTxInvalidString{Fmt("Invalid characters found in NameTx.Name (%s). Only alphanumeric, underscores, dashes, forward slashes, and @ are allowed", tx.Name)}
}
if !validateNameRegEntryData(tx.Data) {
return ErrTxInvalidString{Fmt("Invalid characters found in NameTx.Data (%s). Only the kind of things found in a JSON file are allowed", tx.Data)}
}
return nil
}
func (tx *NameTx) String() string {
return Fmt("NameTx{%v -> %s: %s}", tx.Input, tx.Name, tx.Data)
}
//-----------------------------------------------------------------------------
type BondTx struct {
PubKey acm.PubKeyEd25519 `json:"pub_key"`
Signature acm.SignatureEd25519 `json:"signature"`
Inputs []*TxInput `json:"inputs"`
UnbondTo []*TxOutput `json:"unbond_to"`
}
func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeBond)), w, n, err)
for i, in := range tx.Inputs {
in.WriteSignBytes(w, n, err)
if i != len(tx.Inputs)-1 {
wire.WriteTo([]byte(","), w, n, err)
}
}
wire.WriteTo([]byte(Fmt(`],"pub_key":`)), w, n, err)
wire.WriteTo(wire.JSONBytes(tx.PubKey), w, n, err)
wire.WriteTo([]byte(`,"unbond_to":[`), w, n, err)
for i, out := range tx.UnbondTo {
out.WriteSignBytes(w, n, err)
if i != len(tx.UnbondTo)-1 {
wire.WriteTo([]byte(","), w, n, err)
}
}
wire.WriteTo([]byte(`]}]}`), w, n, err)
}
func (tx *BondTx) String() string {
return Fmt("BondTx{%v: %v -> %v}", tx.PubKey, tx.Inputs, tx.UnbondTo)
}
//-----------------------------------------------------------------------------
type UnbondTx struct {
Address []byte `json:"address"`
Height int `json:"height"`
Signature acm.SignatureEd25519 `json:"signature"`
}
func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeUnbond, tx.Address, tx.Height)), w, n, err)
}
func (tx *UnbondTx) String() string {
return Fmt("UnbondTx{%X,%v,%v}", tx.Address, tx.Height, tx.Signature)
}
//-----------------------------------------------------------------------------
type RebondTx struct {
Address []byte `json:"address"`
Height int `json:"height"`
Signature acm.SignatureEd25519 `json:"signature"`
}
func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeRebond, tx.Address, tx.Height)), w, n, err)
}
func (tx *RebondTx) String() string {
return Fmt("RebondTx{%X,%v,%v}", tx.Address, tx.Height, tx.Signature)
}
//-----------------------------------------------------------------------------
type DupeoutTx struct {
Address []byte `json:"address"`
VoteA Vote `json:"vote_a"`
VoteB Vote `json:"vote_b"`
}
func (tx *DupeoutTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
PanicSanity("DupeoutTx has no sign bytes")
}
func (tx *DupeoutTx) String() string {
return Fmt("DupeoutTx{%X,%v,%v}", tx.Address, tx.VoteA, tx.VoteB)
}
//-----------------------------------------------------------------------------
type PermissionsTx struct {
Input *TxInput `json:"input"`
PermArgs ptypes.PermArgs `json:"args"`
}
func (tx *PermissionsTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"args":"`, TxTypePermissions)), w, n, err)
wire.WriteJSON(tx.PermArgs, w, n, err)
wire.WriteTo([]byte(`","input":`), w, n, err)
tx.Input.WriteSignBytes(w, n, err)
wire.WriteTo([]byte(`}]}`), w, n, err)
}
func (tx *PermissionsTx) String() string {
return Fmt("PermissionsTx{%v -> %v}", tx.Input, tx.PermArgs)
}
//-----------------------------------------------------------------------------
// 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)
}
//--------------------------------------------------------------------------------
// Contract: This function is deterministic and completely reversible.
func jsonEscape(str string) string {
escapedBytes, err := json.Marshal(str)
if err != nil {
PanicSanity(Fmt("Error json-escaping a string", str))
}
return string(escapedBytes)
}
type Tx []byte

+ 0
- 178
types/tx_test.go View File

@ -1,178 +0,0 @@
package types
import (
"testing"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
_ "github.com/tendermint/tendermint/config/tendermint_test"
ptypes "github.com/tendermint/tendermint/permission/types"
)
var chainID string
func init() {
chainID = config.GetString("chain_id")
}
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 := acm.SignBytes(chainID, sendTx)
signStr := string(signBytes)
expected := Fmt(`{"chain_id":"%s","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("chain_id"))
if signStr != expected {
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
}
}
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 := acm.SignBytes(chainID, callTx)
signStr := string(signBytes)
expected := Fmt(`{"chain_id":"%s","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`,
config.GetString("chain_id"))
if signStr != expected {
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
}
}
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)
}
}
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)
}
}
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")
}
}
func TestRebondTxSignable(t *testing.T) {
rebondTx := &RebondTx{
Address: []byte("address1"),
Height: 111,
}
signBytes := acm.SignBytes(chainID, rebondTx)
signStr := string(signBytes)
expected := Fmt(`{"chain_id":"%s","tx":[19,{"address":"6164647265737331","height":111}]}`,
config.GetString("chain_id"))
if signStr != expected {
t.Errorf("Got unexpected sign string for RebondTx")
}
}
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)
}
}

+ 0
- 260
types/tx_utils.go View File

@ -1,260 +0,0 @@
package types
import (
"fmt"
acm "github.com/tendermint/tendermint/account"
ptypes "github.com/tendermint/tendermint/permission/types"
)
type AccountGetter interface {
GetAccount(addr []byte) *acm.Account
}
//----------------------------------------------------------------------------
// SendTx interface for adding inputs/outputs and adding signatures
func NewSendTx() *SendTx {
return &SendTx{
Inputs: []*TxInput{},
Outputs: []*TxOutput{},
}
}
func (tx *SendTx) AddInput(st AccountGetter, pubkey acm.PubKey, amt int64) error {
addr := pubkey.Address()
acc := st.GetAccount(addr)
if acc == nil {
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey)
}
return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1)
}
func (tx *SendTx) AddInputWithNonce(pubkey acm.PubKey, amt int64, nonce int) error {
addr := pubkey.Address()
tx.Inputs = append(tx.Inputs, &TxInput{
Address: addr,
Amount: amt,
Sequence: nonce,
Signature: acm.SignatureEd25519{},
PubKey: pubkey,
})
return nil
}
func (tx *SendTx) AddOutput(addr []byte, amt int64) error {
tx.Outputs = append(tx.Outputs, &TxOutput{
Address: addr,
Amount: amt,
})
return nil
}
func (tx *SendTx) SignInput(chainID string, i int, privAccount *acm.PrivAccount) error {
if i >= len(tx.Inputs) {
return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs))
}
tx.Inputs[i].PubKey = privAccount.PubKey
tx.Inputs[i].Signature = privAccount.Sign(chainID, tx)
return nil
}
//----------------------------------------------------------------------------
// CallTx interface for creating tx
func NewCallTx(st AccountGetter, from acm.PubKey, to, data []byte, amt, gasLimit, fee int64) (*CallTx, error) {
addr := from.Address()
acc := st.GetAccount(addr)
if acc == nil {
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from)
}
nonce := acc.Sequence + 1
return NewCallTxWithNonce(from, to, data, amt, gasLimit, fee, nonce), nil
}
func NewCallTxWithNonce(from acm.PubKey, to, data []byte, amt, gasLimit, fee int64, nonce int) *CallTx {
addr := from.Address()
input := &TxInput{
Address: addr,
Amount: amt,
Sequence: nonce,
Signature: acm.SignatureEd25519{},
PubKey: from,
}
return &CallTx{
Input: input,
Address: to,
GasLimit: gasLimit,
Fee: fee,
Data: data,
}
}
func (tx *CallTx) Sign(chainID string, privAccount *acm.PrivAccount) {
tx.Input.PubKey = privAccount.PubKey
tx.Input.Signature = privAccount.Sign(chainID, tx)
}
//----------------------------------------------------------------------------
// NameTx interface for creating tx
func NewNameTx(st AccountGetter, from acm.PubKey, name, data string, amt, fee int64) (*NameTx, error) {
addr := from.Address()
acc := st.GetAccount(addr)
if acc == nil {
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from)
}
nonce := acc.Sequence + 1
return NewNameTxWithNonce(from, name, data, amt, fee, nonce), nil
}
func NewNameTxWithNonce(from acm.PubKey, name, data string, amt, fee int64, nonce int) *NameTx {
addr := from.Address()
input := &TxInput{
Address: addr,
Amount: amt,
Sequence: nonce,
Signature: acm.SignatureEd25519{},
PubKey: from,
}
return &NameTx{
Input: input,
Name: name,
Data: data,
Fee: fee,
}
}
func (tx *NameTx) Sign(chainID string, privAccount *acm.PrivAccount) {
tx.Input.PubKey = privAccount.PubKey
tx.Input.Signature = privAccount.Sign(chainID, tx)
}
//----------------------------------------------------------------------------
// BondTx interface for adding inputs/outputs and adding signatures
func NewBondTx(pubkey acm.PubKey) (*BondTx, error) {
pubkeyEd, ok := pubkey.(acm.PubKeyEd25519)
if !ok {
return nil, fmt.Errorf("Pubkey must be ed25519")
}
return &BondTx{
PubKey: pubkeyEd,
Inputs: []*TxInput{},
UnbondTo: []*TxOutput{},
}, nil
}
func (tx *BondTx) AddInput(st AccountGetter, pubkey acm.PubKey, amt int64) error {
addr := pubkey.Address()
acc := st.GetAccount(addr)
if acc == nil {
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey)
}
return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1)
}
func (tx *BondTx) AddInputWithNonce(pubkey acm.PubKey, amt int64, nonce int) error {
addr := pubkey.Address()
tx.Inputs = append(tx.Inputs, &TxInput{
Address: addr,
Amount: amt,
Sequence: nonce,
Signature: acm.SignatureEd25519{},
PubKey: pubkey,
})
return nil
}
func (tx *BondTx) AddOutput(addr []byte, amt int64) error {
tx.UnbondTo = append(tx.UnbondTo, &TxOutput{
Address: addr,
Amount: amt,
})
return nil
}
func (tx *BondTx) SignBond(chainID string, privAccount *acm.PrivAccount) error {
sig := privAccount.Sign(chainID, tx)
sigEd, ok := sig.(acm.SignatureEd25519)
if !ok {
return fmt.Errorf("Bond signer must be ED25519")
}
tx.Signature = sigEd
return nil
}
func (tx *BondTx) SignInput(chainID string, i int, privAccount *acm.PrivAccount) error {
if i >= len(tx.Inputs) {
return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs))
}
tx.Inputs[i].PubKey = privAccount.PubKey
tx.Inputs[i].Signature = privAccount.Sign(chainID, tx)
return nil
}
//----------------------------------------------------------------------
// UnbondTx interface for creating tx
func NewUnbondTx(addr []byte, height int) *UnbondTx {
return &UnbondTx{
Address: addr,
Height: height,
}
}
func (tx *UnbondTx) Sign(chainID string, privAccount *acm.PrivAccount) {
tx.Signature = privAccount.Sign(chainID, tx).(acm.SignatureEd25519)
}
//----------------------------------------------------------------------
// RebondTx interface for creating tx
func NewRebondTx(addr []byte, height int) *RebondTx {
return &RebondTx{
Address: addr,
Height: height,
}
}
func (tx *RebondTx) Sign(chainID string, privAccount *acm.PrivAccount) {
tx.Signature = privAccount.Sign(chainID, tx).(acm.SignatureEd25519)
}
//----------------------------------------------------------------------------
// PermissionsTx interface for creating tx
func NewPermissionsTx(st AccountGetter, from acm.PubKey, args ptypes.PermArgs) (*PermissionsTx, error) {
addr := from.Address()
acc := st.GetAccount(addr)
if acc == nil {
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from)
}
nonce := acc.Sequence + 1
return NewPermissionsTxWithNonce(from, args, nonce), nil
}
func NewPermissionsTxWithNonce(from acm.PubKey, args ptypes.PermArgs, nonce int) *PermissionsTx {
addr := from.Address()
input := &TxInput{
Address: addr,
Amount: 1, // NOTE: amounts can't be 0 ...
Sequence: nonce,
Signature: acm.SignatureEd25519{},
PubKey: from,
}
return &PermissionsTx{
Input: input,
PermArgs: args,
}
}
func (tx *PermissionsTx) Sign(chainID string, privAccount *acm.PrivAccount) {
tx.Input.PubKey = privAccount.PubKey
tx.Input.Signature = privAccount.Sign(chainID, tx)
}

+ 6
- 42
types/validator.go View File

@ -5,54 +5,20 @@ import (
"fmt"
"io"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
)
// Persistent (mostly) static data for each Validator
type ValidatorInfo struct {
Address []byte `json:"address"`
PubKey acm.PubKeyEd25519 `json:"pub_key"`
UnbondTo []*TxOutput `json:"unbond_to"`
FirstBondHeight int `json:"first_bond_height"`
FirstBondAmount int64 `json:"first_bond_amount"`
DestroyedHeight int `json:"destroyed_height"` // If destroyed
DestroyedAmount int64 `json:"destroyed_amount"` // If destroyed
ReleasedHeight int `json:"released_height"` // If released
}
func (valInfo *ValidatorInfo) Copy() *ValidatorInfo {
valInfoCopy := *valInfo
return &valInfoCopy
}
func ValidatorInfoEncoder(o interface{}, w io.Writer, n *int64, err *error) {
wire.WriteBinary(o.(*ValidatorInfo), w, n, err)
}
func ValidatorInfoDecoder(r io.Reader, n *int64, err *error) interface{} {
return wire.ReadBinary(&ValidatorInfo{}, r, n, err)
}
var ValidatorInfoCodec = wire.Codec{
Encode: ValidatorInfoEncoder,
Decode: ValidatorInfoDecoder,
}
//-----------------------------------------------------------------------------
// Volatile state for each Validator
// Also persisted with the state, but fields change
// every height|round so they don't go in merkle.Tree
type Validator struct {
Address []byte `json:"address"`
PubKey acm.PubKeyEd25519 `json:"pub_key"`
BondHeight int `json:"bond_height"`
UnbondHeight int `json:"unbond_height"`
LastCommitHeight int `json:"last_commit_height"`
VotingPower int64 `json:"voting_power"`
Accum int64 `json:"accum"`
Address []byte `json:"address"`
PubKey crypto.PubKeyEd25519 `json:"pub_key"`
LastCommitHeight int `json:"last_commit_height"`
VotingPower int64 `json:"voting_power"`
Accum int64 `json:"accum"`
}
// Creates a new copy of the validator so we can mutate accum.
@ -90,9 +56,7 @@ func (v *Validator) String() string {
return fmt.Sprintf("Validator{%X %v %v-%v-%v VP:%v A:%v}",
v.Address,
v.PubKey,
v.BondHeight,
v.LastCommitHeight,
v.UnbondHeight,
v.VotingPower,
v.Accum)
}


+ 1
- 2
types/validator_set.go View File

@ -6,7 +6,6 @@ import (
"sort"
"strings"
"github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-merkle"
)
@ -230,7 +229,7 @@ func (valSet *ValidatorSet) VerifyValidation(chainID string,
}
_, val := valSet.GetByIndex(idx)
// Validate signature
precommitSignBytes := account.SignBytes(chainID, precommit)
precommitSignBytes := SignBytes(chainID, precommit)
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
return fmt.Errorf("Invalid validation -- invalid signature: %v", precommit)
}


+ 7
- 10
types/validator_set_test.go View File

@ -1,25 +1,24 @@
package types
import (
"github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto"
"bytes"
"strings"
"testing"
)
func randPubKey() account.PubKeyEd25519 {
func randPubKey() crypto.PubKeyEd25519 {
var pubKey [32]byte
copy(pubKey[:], RandBytes(32))
return account.PubKeyEd25519(pubKey)
return crypto.PubKeyEd25519(pubKey)
}
func randValidator_() *Validator {
return &Validator{
Address: RandBytes(20),
PubKey: randPubKey(),
BondHeight: RandInt(),
VotingPower: RandInt64(),
Accum: RandInt64(),
}
@ -53,21 +52,18 @@ func TestProposerSelection(t *testing.T) {
&Validator{
Address: []byte("foo"),
PubKey: randPubKey(),
BondHeight: RandInt(),
VotingPower: 1000,
Accum: 0,
},
&Validator{
Address: []byte("bar"),
PubKey: randPubKey(),
BondHeight: RandInt(),
VotingPower: 300,
Accum: 0,
},
&Validator{
Address: []byte("baz"),
PubKey: randPubKey(),
BondHeight: RandInt(),
VotingPower: 330,
Accum: 0,
},
@ -88,10 +84,11 @@ func BenchmarkValidatorSetCopy(b *testing.B) {
b.StopTimer()
vset := NewValidatorSet([]*Validator{})
for i := 0; i < 1000; i++ {
privAccount := account.GenPrivAccount()
privKey := crypto.GenPrivKeyEd25519()
pubKey := privKey.PubKey().(crypto.PubKeyEd25519)
val := &Validator{
Address: privAccount.Address,
PubKey: privAccount.PubKey.(account.PubKeyEd25519),
Address: pubKey.Address(),
PubKey: pubKey,
}
if !vset.Add(val) {
panic("Failed to add validator")


+ 2
- 2
types/vote.go View File

@ -5,7 +5,7 @@ import (
"fmt"
"io"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/go-crypto"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
)
@ -33,7 +33,7 @@ type Vote struct {
Type byte `json:"type"`
BlockHash []byte `json:"block_hash"` // empty if vote is nil.
BlockPartsHeader PartSetHeader `json:"block_parts_header"` // zero if vote is nil.
Signature acm.SignatureEd25519 `json:"signature"`
Signature crypto.SignatureEd25519 `json:"signature"`
}
// Types of votes


+ 9
- 22
types/vote_set.go View File

@ -6,7 +6,6 @@ import (
"strings"
"sync"
acm "github.com/tendermint/tendermint/account"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
)
@ -130,7 +129,7 @@ func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool,
}
// Check signature.
if !val.PubKey.VerifyBytes(acm.SignBytes(config.GetString("chain_id"), vote), vote.Signature) {
if !val.PubKey.VerifyBytes(SignBytes(config.GetString("chain_id"), vote), vote.Signature) {
// Bad signature.
return false, 0, ErrVoteInvalidSignature
}
@ -305,32 +304,20 @@ func (voteSet *VoteSet) MakeValidation() *Validation {
//--------------------------------------------------------------------------------
// For testing...
func RandValidator(randBonded bool, minBonded int64) (*ValidatorInfo, *Validator, *PrivValidator) {
func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidator) {
privVal := GenPrivValidator()
_, tempFilePath := Tempfile("priv_validator_")
privVal.SetFile(tempFilePath)
bonded := minBonded
if randBonded {
bonded += int64(RandUint32())
}
valInfo := &ValidatorInfo{
Address: privVal.Address,
PubKey: privVal.PubKey,
UnbondTo: []*TxOutput{&TxOutput{
Amount: bonded,
Address: privVal.Address,
}},
FirstBondHeight: 0,
FirstBondAmount: bonded,
votePower := minPower
if randPower {
votePower += int64(RandUint32())
}
val := &Validator{
Address: valInfo.Address,
PubKey: valInfo.PubKey,
BondHeight: 0,
UnbondHeight: 0,
Address: privVal.Address,
PubKey: privVal.PubKey,
LastCommitHeight: 0,
VotingPower: valInfo.FirstBondAmount,
VotingPower: votePower,
Accum: 0,
}
return valInfo, val, privVal
return val, privVal
}

+ 1
- 1
types/vote_set_test.go View File

@ -15,7 +15,7 @@ func randVoteSet(height int, round int, type_ byte, numValidators int, votingPow
vals := make([]*Validator, numValidators)
privValidators := make([]*PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
_, val, privValidator := RandValidator(false, votingPower)
val, privValidator := RandValidator(false, votingPower)
vals[i] = val
privValidators[i] = privValidator
}


+ 0
- 0
vm/.ethtest View File


+ 0
- 26
vm/common.go View File

@ -1,26 +0,0 @@
package vm
import (
"math/big"
)
// To256
//
// "cast" the big int to a 256 big int (i.e., limit to)
var tt256 = new(big.Int).Lsh(big.NewInt(1), 256)
var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
var tt255 = new(big.Int).Lsh(big.NewInt(1), 255)
func U256(x *big.Int) *big.Int {
x.And(x, tt256m1)
return x
}
func S256(x *big.Int) *big.Int {
if x.Cmp(tt255) < 0 {
return x
} else {
// We don't want to modify x, ever
return new(big.Int).Sub(x, tt256)
}
}

+ 0
- 18
vm/gas.go View File

@ -1,18 +0,0 @@
package vm
const (
GasSha3 int64 = 1
GasGetAccount int64 = 1
GasStorageUpdate int64 = 1
GasBaseOp int64 = 0 // TODO: make this 1
GasStackOp int64 = 1
GasEcRecover int64 = 1
GasSha256Word int64 = 1
GasSha256Base int64 = 1
GasRipemd160Word int64 = 1
GasRipemd160Base int64 = 1
GasIdentityWord int64 = 1
GasIdentityBase int64 = 1
)

+ 0
- 7
vm/log.go View File

@ -1,7 +0,0 @@
package vm
import (
"github.com/tendermint/go-logger"
)
var log = logger.New("module", "vm")

+ 0
- 105
vm/native.go View File

@ -1,105 +0,0 @@
package vm
import (
"crypto/sha256"
"code.google.com/p/go.crypto/ripemd160"
. "github.com/tendermint/go-common"
)
var registeredNativeContracts = make(map[Word256]NativeContract)
func RegisteredNativeContract(addr Word256) bool {
_, ok := registeredNativeContracts[addr]
return ok
}
func RegisterNativeContract(addr Word256, fn NativeContract) bool {
_, exists := registeredNativeContracts[addr]
if exists {
return false
}
registeredNativeContracts[addr] = fn
return true
}
func init() {
registerNativeContracts()
registerSNativeContracts()
}
func registerNativeContracts() {
// registeredNativeContracts[Int64ToWord256(1)] = ecrecoverFunc
registeredNativeContracts[Int64ToWord256(2)] = sha256Func
registeredNativeContracts[Int64ToWord256(3)] = ripemd160Func
registeredNativeContracts[Int64ToWord256(4)] = identityFunc
}
//-----------------------------------------------------------------------------
type NativeContract func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error)
/* Removed due to C dependency
func ecrecoverFunc(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) {
// Deduct gas
gasRequired := GasEcRecover
if *gas < gasRequired {
return nil, ErrInsufficientGas
} else {
*gas -= gasRequired
}
// Recover
hash := input[:32]
v := byte(input[32] - 27) // ignore input[33:64], v is small.
sig := append(input[64:], v)
recovered, err := secp256k1.RecoverPubkey(hash, sig)
if err != nil {
return nil, err
}
hashed := sha3.Sha3(recovered[1:])
return LeftPadBytes(hashed, 32), nil
}
*/
func sha256Func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) {
// Deduct gas
gasRequired := int64((len(input)+31)/32)*GasSha256Word + GasSha256Base
if *gas < gasRequired {
return nil, ErrInsufficientGas
} else {
*gas -= gasRequired
}
// Hash
hasher := sha256.New()
// CONTRACT: this does not err
hasher.Write(input)
return hasher.Sum(nil), nil
}
func ripemd160Func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) {
// Deduct gas
gasRequired := int64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base
if *gas < gasRequired {
return nil, ErrInsufficientGas
} else {
*gas -= gasRequired
}
// Hash
hasher := ripemd160.New()
// CONTRACT: this does not err
hasher.Write(input)
return LeftPadBytes(hasher.Sum(nil), 32), nil
}
func identityFunc(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) {
// Deduct gas
gasRequired := int64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase
if *gas < gasRequired {
return nil, ErrInsufficientGas
} else {
*gas -= gasRequired
}
// Return identity
return input, nil
}

+ 0
- 354
vm/opcodes.go View File

@ -1,354 +0,0 @@
package vm
import (
"fmt"
"gopkg.in/fatih/set.v0"
)
type OpCode byte
const (
// Op codes
// 0x0 range - arithmetic ops
STOP OpCode = iota
ADD
MUL
SUB
DIV
SDIV
MOD
SMOD
ADDMOD
MULMOD
EXP
SIGNEXTEND
)
const (
LT OpCode = iota + 0x10
GT
SLT
SGT
EQ
ISZERO
AND
OR
XOR
NOT
BYTE
SHA3 = 0x20
)
const (
// 0x30 range - closure state
ADDRESS OpCode = 0x30 + iota
BALANCE
ORIGIN
CALLER
CALLVALUE
CALLDATALOAD
CALLDATASIZE
CALLDATACOPY
CODESIZE
CODECOPY
GASPRICE_DEPRECATED
EXTCODESIZE
EXTCODECOPY
)
const (
// 0x40 range - block operations
BLOCKHASH OpCode = 0x40 + iota
COINBASE
TIMESTAMP
BLOCKHEIGHT
DIFFICULTY_DEPRECATED
GASLIMIT
)
const (
// 0x50 range - 'storage' and execution
POP OpCode = 0x50 + iota
MLOAD
MSTORE
MSTORE8
SLOAD
SSTORE
JUMP
JUMPI
PC
MSIZE
GAS
JUMPDEST
)
const (
// 0x60 range
PUSH1 OpCode = 0x60 + iota
PUSH2
PUSH3
PUSH4
PUSH5
PUSH6
PUSH7
PUSH8
PUSH9
PUSH10
PUSH11
PUSH12
PUSH13
PUSH14
PUSH15
PUSH16
PUSH17
PUSH18
PUSH19
PUSH20
PUSH21
PUSH22
PUSH23
PUSH24
PUSH25
PUSH26
PUSH27
PUSH28
PUSH29
PUSH30
PUSH31
PUSH32
DUP1
DUP2
DUP3
DUP4
DUP5
DUP6
DUP7
DUP8
DUP9
DUP10
DUP11
DUP12
DUP13
DUP14
DUP15
DUP16
SWAP1
SWAP2
SWAP3
SWAP4
SWAP5
SWAP6
SWAP7
SWAP8
SWAP9
SWAP10
SWAP11
SWAP12
SWAP13
SWAP14
SWAP15
SWAP16
)
const (
LOG0 OpCode = 0xa0 + iota
LOG1
LOG2
LOG3
LOG4
)
const (
// 0xf0 range - closures
CREATE OpCode = 0xf0 + iota
CALL
CALLCODE
RETURN
// 0x70 range - other
SUICIDE = 0xff
)
// Since the opcodes aren't all in order we can't use a regular slice
var opCodeToString = map[OpCode]string{
// 0x0 range - arithmetic ops
STOP: "STOP",
ADD: "ADD",
MUL: "MUL",
SUB: "SUB",
DIV: "DIV",
SDIV: "SDIV",
MOD: "MOD",
SMOD: "SMOD",
EXP: "EXP",
NOT: "NOT",
LT: "LT",
GT: "GT",
SLT: "SLT",
SGT: "SGT",
EQ: "EQ",
ISZERO: "ISZERO",
SIGNEXTEND: "SIGNEXTEND",
// 0x10 range - bit ops
AND: "AND",
OR: "OR",
XOR: "XOR",
BYTE: "BYTE",
ADDMOD: "ADDMOD",
MULMOD: "MULMOD",
// 0x20 range - crypto
SHA3: "SHA3",
// 0x30 range - closure state
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
CALLER: "CALLER",
CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE_DEPRECATED: "TXGASPRICE_DEPRECATED",
// 0x40 range - block operations
BLOCKHASH: "BLOCKHASH",
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
BLOCKHEIGHT: "BLOCKHEIGHT",
DIFFICULTY_DEPRECATED: "DIFFICULTY_DEPRECATED",
GASLIMIT: "GASLIMIT",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
// 0x50 range - 'storage' and execution
POP: "POP",
//DUP: "DUP",
//SWAP: "SWAP",
MLOAD: "MLOAD",
MSTORE: "MSTORE",
MSTORE8: "MSTORE8",
SLOAD: "SLOAD",
SSTORE: "SSTORE",
JUMP: "JUMP",
JUMPI: "JUMPI",
PC: "PC",
MSIZE: "MSIZE",
GAS: "GAS",
JUMPDEST: "JUMPDEST",
// 0x60 range - push
PUSH1: "PUSH1",
PUSH2: "PUSH2",
PUSH3: "PUSH3",
PUSH4: "PUSH4",
PUSH5: "PUSH5",
PUSH6: "PUSH6",
PUSH7: "PUSH7",
PUSH8: "PUSH8",
PUSH9: "PUSH9",
PUSH10: "PUSH10",
PUSH11: "PUSH11",
PUSH12: "PUSH12",
PUSH13: "PUSH13",
PUSH14: "PUSH14",
PUSH15: "PUSH15",
PUSH16: "PUSH16",
PUSH17: "PUSH17",
PUSH18: "PUSH18",
PUSH19: "PUSH19",
PUSH20: "PUSH20",
PUSH21: "PUSH21",
PUSH22: "PUSH22",
PUSH23: "PUSH23",
PUSH24: "PUSH24",
PUSH25: "PUSH25",
PUSH26: "PUSH26",
PUSH27: "PUSH27",
PUSH28: "PUSH28",
PUSH29: "PUSH29",
PUSH30: "PUSH30",
PUSH31: "PUSH31",
PUSH32: "PUSH32",
DUP1: "DUP1",
DUP2: "DUP2",
DUP3: "DUP3",
DUP4: "DUP4",
DUP5: "DUP5",
DUP6: "DUP6",
DUP7: "DUP7",
DUP8: "DUP8",
DUP9: "DUP9",
DUP10: "DUP10",
DUP11: "DUP11",
DUP12: "DUP12",
DUP13: "DUP13",
DUP14: "DUP14",
DUP15: "DUP15",
DUP16: "DUP16",
SWAP1: "SWAP1",
SWAP2: "SWAP2",
SWAP3: "SWAP3",
SWAP4: "SWAP4",
SWAP5: "SWAP5",
SWAP6: "SWAP6",
SWAP7: "SWAP7",
SWAP8: "SWAP8",
SWAP9: "SWAP9",
SWAP10: "SWAP10",
SWAP11: "SWAP11",
SWAP12: "SWAP12",
SWAP13: "SWAP13",
SWAP14: "SWAP14",
SWAP15: "SWAP15",
SWAP16: "SWAP16",
LOG0: "LOG0",
LOG1: "LOG1",
LOG2: "LOG2",
LOG3: "LOG3",
LOG4: "LOG4",
// 0xf0 range
CREATE: "CREATE",
CALL: "CALL",
RETURN: "RETURN",
CALLCODE: "CALLCODE",
// 0x70 range - other
SUICIDE: "SUICIDE",
}
func (o OpCode) String() string {
str := opCodeToString[o]
if len(str) == 0 {
return fmt.Sprintf("Missing opcode 0x%x", int(o))
}
return str
}
//-----------------------------------------------------------------------------
func AnalyzeJumpDests(code []byte) (dests *set.Set) {
dests = set.New()
for pc := uint64(0); pc < uint64(len(code)); pc++ {
var op OpCode = OpCode(code[pc])
switch op {
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
a := uint64(op) - uint64(PUSH1) + 1
pc += a
case JUMPDEST:
dests.Add(pc)
}
}
return
}

+ 0
- 35
vm/randentropy/rand_entropy.go View File

@ -1,35 +0,0 @@
package randentropy
import (
crand "crypto/rand"
"github.com/tendermint/tendermint/vm/sha3"
"io"
)
var Reader io.Reader = &randEntropy{}
type randEntropy struct {
}
func (*randEntropy) Read(bytes []byte) (n int, err error) {
readBytes := GetEntropyCSPRNG(len(bytes))
copy(bytes, readBytes)
return len(bytes), nil
}
// TODO: copied from crypto.go , move to sha3 package?
func Sha3(data []byte) []byte {
d := sha3.NewKeccak256()
d.Write(data)
return d.Sum(nil)
}
func GetEntropyCSPRNG(n int) []byte {
mainBuff := make([]byte, n)
_, err := io.ReadFull(crand.Reader, mainBuff)
if err != nil {
panic("reading from crypto/rand failed: " + err.Error())
}
return mainBuff
}

+ 0
- 171
vm/sha3/keccakf.go View File

@ -1,171 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha3
// This file implements the core Keccak permutation function necessary for computing SHA3.
// This is implemented in a separate file to allow for replacement by an optimized implementation.
// Nothing in this package is exported.
// For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/).
// rc stores the round constants for use in the ι step.
var rc = [...]uint64{
0x0000000000000001,
0x0000000000008082,
0x800000000000808A,
0x8000000080008000,
0x000000000000808B,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008A,
0x0000000000000088,
0x0000000080008009,
0x000000008000000A,
0x000000008000808B,
0x800000000000008B,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800A,
0x800000008000000A,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
}
// ro_xx represent the rotation offsets for use in the χ step.
// Defining them as const instead of in an array allows the compiler to insert constant shifts.
const (
ro_00 = 0
ro_01 = 36
ro_02 = 3
ro_03 = 41
ro_04 = 18
ro_05 = 1
ro_06 = 44
ro_07 = 10
ro_08 = 45
ro_09 = 2
ro_10 = 62
ro_11 = 6
ro_12 = 43
ro_13 = 15
ro_14 = 61
ro_15 = 28
ro_16 = 55
ro_17 = 25
ro_18 = 21
ro_19 = 56
ro_20 = 27
ro_21 = 20
ro_22 = 39
ro_23 = 8
ro_24 = 14
)
// keccakF computes the complete Keccak-f function consisting of 24 rounds with a different
// constant (rc) in each round. This implementation fully unrolls the round function to avoid
// inner loops, as well as pre-calculating shift offsets.
func (d *digest) keccakF() {
for _, roundConstant := range rc {
// θ step
d.c[0] = d.a[0] ^ d.a[5] ^ d.a[10] ^ d.a[15] ^ d.a[20]
d.c[1] = d.a[1] ^ d.a[6] ^ d.a[11] ^ d.a[16] ^ d.a[21]
d.c[2] = d.a[2] ^ d.a[7] ^ d.a[12] ^ d.a[17] ^ d.a[22]
d.c[3] = d.a[3] ^ d.a[8] ^ d.a[13] ^ d.a[18] ^ d.a[23]
d.c[4] = d.a[4] ^ d.a[9] ^ d.a[14] ^ d.a[19] ^ d.a[24]
d.d[0] = d.c[4] ^ (d.c[1]<<1 ^ d.c[1]>>63)
d.d[1] = d.c[0] ^ (d.c[2]<<1 ^ d.c[2]>>63)
d.d[2] = d.c[1] ^ (d.c[3]<<1 ^ d.c[3]>>63)
d.d[3] = d.c[2] ^ (d.c[4]<<1 ^ d.c[4]>>63)
d.d[4] = d.c[3] ^ (d.c[0]<<1 ^ d.c[0]>>63)
d.a[0] ^= d.d[0]
d.a[1] ^= d.d[1]
d.a[2] ^= d.d[2]
d.a[3] ^= d.d[3]
d.a[4] ^= d.d[4]
d.a[5] ^= d.d[0]
d.a[6] ^= d.d[1]
d.a[7] ^= d.d[2]
d.a[8] ^= d.d[3]
d.a[9] ^= d.d[4]
d.a[10] ^= d.d[0]
d.a[11] ^= d.d[1]
d.a[12] ^= d.d[2]
d.a[13] ^= d.d[3]
d.a[14] ^= d.d[4]
d.a[15] ^= d.d[0]
d.a[16] ^= d.d[1]
d.a[17] ^= d.d[2]
d.a[18] ^= d.d[3]
d.a[19] ^= d.d[4]
d.a[20] ^= d.d[0]
d.a[21] ^= d.d[1]
d.a[22] ^= d.d[2]
d.a[23] ^= d.d[3]
d.a[24] ^= d.d[4]
// ρ and π steps
d.b[0] = d.a[0]
d.b[1] = d.a[6]<<ro_06 ^ d.a[6]>>(64-ro_06)
d.b[2] = d.a[12]<<ro_12 ^ d.a[12]>>(64-ro_12)
d.b[3] = d.a[18]<<ro_18 ^ d.a[18]>>(64-ro_18)
d.b[4] = d.a[24]<<ro_24 ^ d.a[24]>>(64-ro_24)
d.b[5] = d.a[3]<<ro_15 ^ d.a[3]>>(64-ro_15)
d.b[6] = d.a[9]<<ro_21 ^ d.a[9]>>(64-ro_21)
d.b[7] = d.a[10]<<ro_02 ^ d.a[10]>>(64-ro_02)
d.b[8] = d.a[16]<<ro_08 ^ d.a[16]>>(64-ro_08)
d.b[9] = d.a[22]<<ro_14 ^ d.a[22]>>(64-ro_14)
d.b[10] = d.a[1]<<ro_05 ^ d.a[1]>>(64-ro_05)
d.b[11] = d.a[7]<<ro_11 ^ d.a[7]>>(64-ro_11)
d.b[12] = d.a[13]<<ro_17 ^ d.a[13]>>(64-ro_17)
d.b[13] = d.a[19]<<ro_23 ^ d.a[19]>>(64-ro_23)
d.b[14] = d.a[20]<<ro_04 ^ d.a[20]>>(64-ro_04)
d.b[15] = d.a[4]<<ro_20 ^ d.a[4]>>(64-ro_20)
d.b[16] = d.a[5]<<ro_01 ^ d.a[5]>>(64-ro_01)
d.b[17] = d.a[11]<<ro_07 ^ d.a[11]>>(64-ro_07)
d.b[18] = d.a[17]<<ro_13 ^ d.a[17]>>(64-ro_13)
d.b[19] = d.a[23]<<ro_19 ^ d.a[23]>>(64-ro_19)
d.b[20] = d.a[2]<<ro_10 ^ d.a[2]>>(64-ro_10)
d.b[21] = d.a[8]<<ro_16 ^ d.a[8]>>(64-ro_16)
d.b[22] = d.a[14]<<ro_22 ^ d.a[14]>>(64-ro_22)
d.b[23] = d.a[15]<<ro_03 ^ d.a[15]>>(64-ro_03)
d.b[24] = d.a[21]<<ro_09 ^ d.a[21]>>(64-ro_09)
// χ step
d.a[0] = d.b[0] ^ (^d.b[1] & d.b[2])
d.a[1] = d.b[1] ^ (^d.b[2] & d.b[3])
d.a[2] = d.b[2] ^ (^d.b[3] & d.b[4])
d.a[3] = d.b[3] ^ (^d.b[4] & d.b[0])
d.a[4] = d.b[4] ^ (^d.b[0] & d.b[1])
d.a[5] = d.b[5] ^ (^d.b[6] & d.b[7])
d.a[6] = d.b[6] ^ (^d.b[7] & d.b[8])
d.a[7] = d.b[7] ^ (^d.b[8] & d.b[9])
d.a[8] = d.b[8] ^ (^d.b[9] & d.b[5])
d.a[9] = d.b[9] ^ (^d.b[5] & d.b[6])
d.a[10] = d.b[10] ^ (^d.b[11] & d.b[12])
d.a[11] = d.b[11] ^ (^d.b[12] & d.b[13])
d.a[12] = d.b[12] ^ (^d.b[13] & d.b[14])
d.a[13] = d.b[13] ^ (^d.b[14] & d.b[10])
d.a[14] = d.b[14] ^ (^d.b[10] & d.b[11])
d.a[15] = d.b[15] ^ (^d.b[16] & d.b[17])
d.a[16] = d.b[16] ^ (^d.b[17] & d.b[18])
d.a[17] = d.b[17] ^ (^d.b[18] & d.b[19])
d.a[18] = d.b[18] ^ (^d.b[19] & d.b[15])
d.a[19] = d.b[19] ^ (^d.b[15] & d.b[16])
d.a[20] = d.b[20] ^ (^d.b[21] & d.b[22])
d.a[21] = d.b[21] ^ (^d.b[22] & d.b[23])
d.a[22] = d.b[22] ^ (^d.b[23] & d.b[24])
d.a[23] = d.b[23] ^ (^d.b[24] & d.b[20])
d.a[24] = d.b[24] ^ (^d.b[20] & d.b[21])
// ι step
d.a[0] ^= roundConstant
}
}

+ 0
- 224
vm/sha3/sha3.go View File

@ -1,224 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sha3 implements the SHA3 hash algorithm (formerly called Keccak) chosen by NIST in 2012.
// This file provides a SHA3 implementation which implements the standard hash.Hash interface.
// Writing input data, including padding, and reading output data are computed in this file.
// Note that the current implementation can compute the hash of an integral number of bytes only.
// This is a consequence of the hash interface in which a buffer of bytes is passed in.
// The internals of the Keccak-f function are computed in keccakf.go.
// For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/).
package sha3
import (
"encoding/binary"
"hash"
)
// laneSize is the size in bytes of each "lane" of the internal state of SHA3 (5 * 5 * 8).
// Note that changing this size would requires using a type other than uint64 to store each lane.
const laneSize = 8
// sliceSize represents the dimensions of the internal state, a square matrix of
// sliceSize ** 2 lanes. This is the size of both the "rows" and "columns" dimensions in the
// terminology of the SHA3 specification.
const sliceSize = 5
// numLanes represents the total number of lanes in the state.
const numLanes = sliceSize * sliceSize
// stateSize is the size in bytes of the internal state of SHA3 (5 * 5 * WSize).
const stateSize = laneSize * numLanes
// digest represents the partial evaluation of a checksum.
// Note that capacity, and not outputSize, is the critical security parameter, as SHA3 can output
// an arbitrary number of bytes for any given capacity. The Keccak proposal recommends that
// capacity = 2*outputSize to ensure that finding a collision of size outputSize requires
// O(2^{outputSize/2}) computations (the birthday lower bound). Future standards may modify the
// capacity/outputSize ratio to allow for more output with lower cryptographic security.
type digest struct {
a [numLanes]uint64 // main state of the hash
b [numLanes]uint64 // intermediate states
c [sliceSize]uint64 // intermediate states
d [sliceSize]uint64 // intermediate states
outputSize int // desired output size in bytes
capacity int // number of bytes to leave untouched during squeeze/absorb
absorbed int // number of bytes absorbed thus far
}
// minInt returns the lesser of two integer arguments, to simplify the absorption routine.
func minInt(v1, v2 int) int {
if v1 <= v2 {
return v1
}
return v2
}
// rate returns the number of bytes of the internal state which can be absorbed or squeezed
// in between calls to the permutation function.
func (d *digest) rate() int {
return stateSize - d.capacity
}
// Reset clears the internal state by zeroing bytes in the state buffer.
// This can be skipped for a newly-created hash state; the default zero-allocated state is correct.
func (d *digest) Reset() {
d.absorbed = 0
for i := range d.a {
d.a[i] = 0
}
}
// BlockSize, required by the hash.Hash interface, does not have a standard intepretation
// for a sponge-based construction like SHA3. We return the data rate: the number of bytes which
// can be absorbed per invocation of the permutation function. For Merkle-Damgård based hashes
// (ie SHA1, SHA2, MD5) the output size of the internal compression function is returned.
// We consider this to be roughly equivalent because it represents the number of bytes of output
// produced per cryptographic operation.
func (d *digest) BlockSize() int { return d.rate() }
// Size returns the output size of the hash function in bytes.
func (d *digest) Size() int {
return d.outputSize
}
// unalignedAbsorb is a helper function for Write, which absorbs data that isn't aligned with an
// 8-byte lane. This requires shifting the individual bytes into position in a uint64.
func (d *digest) unalignedAbsorb(p []byte) {
var t uint64
for i := len(p) - 1; i >= 0; i-- {
t <<= 8
t |= uint64(p[i])
}
offset := (d.absorbed) % d.rate()
t <<= 8 * uint(offset%laneSize)
d.a[offset/laneSize] ^= t
d.absorbed += len(p)
}
// Write "absorbs" bytes into the state of the SHA3 hash, updating as needed when the sponge
// "fills up" with rate() bytes. Since lanes are stored internally as type uint64, this requires
// converting the incoming bytes into uint64s using a little endian interpretation. This
// implementation is optimized for large, aligned writes of multiples of 8 bytes (laneSize).
// Non-aligned or uneven numbers of bytes require shifting and are slower.
func (d *digest) Write(p []byte) (int, error) {
// An initial offset is needed if the we aren't absorbing to the first lane initially.
offset := d.absorbed % d.rate()
toWrite := len(p)
// The first lane may need to absorb unaligned and/or incomplete data.
if (offset%laneSize != 0 || len(p) < 8) && len(p) > 0 {
toAbsorb := minInt(laneSize-(offset%laneSize), len(p))
d.unalignedAbsorb(p[:toAbsorb])
p = p[toAbsorb:]
offset = (d.absorbed) % d.rate()
// For every rate() bytes absorbed, the state must be permuted via the F Function.
if (d.absorbed)%d.rate() == 0 {
d.keccakF()
}
}
// This loop should absorb the bulk of the data into full, aligned lanes.
// It will call the update function as necessary.
for len(p) > 7 {
firstLane := offset / laneSize
lastLane := minInt(d.rate()/laneSize, firstLane+len(p)/laneSize)
// This inner loop absorbs input bytes into the state in groups of 8, converted to uint64s.
for lane := firstLane; lane < lastLane; lane++ {
d.a[lane] ^= binary.LittleEndian.Uint64(p[:laneSize])
p = p[laneSize:]
}
d.absorbed += (lastLane - firstLane) * laneSize
// For every rate() bytes absorbed, the state must be permuted via the F Function.
if (d.absorbed)%d.rate() == 0 {
d.keccakF()
}
offset = 0
}
// If there are insufficient bytes to fill the final lane, an unaligned absorption.
// This should always start at a correct lane boundary though, or else it would be caught
// by the uneven opening lane case above.
if len(p) > 0 {
d.unalignedAbsorb(p)
}
return toWrite, nil
}
// pad computes the SHA3 padding scheme based on the number of bytes absorbed.
// The padding is a 1 bit, followed by an arbitrary number of 0s and then a final 1 bit, such that
// the input bits plus padding bits are a multiple of rate(). Adding the padding simply requires
// xoring an opening and closing bit into the appropriate lanes.
func (d *digest) pad() {
offset := d.absorbed % d.rate()
// The opening pad bit must be shifted into position based on the number of bytes absorbed
padOpenLane := offset / laneSize
d.a[padOpenLane] ^= 0x0000000000000001 << uint(8*(offset%laneSize))
// The closing padding bit is always in the last position
padCloseLane := (d.rate() / laneSize) - 1
d.a[padCloseLane] ^= 0x8000000000000000
}
// finalize prepares the hash to output data by padding and one final permutation of the state.
func (d *digest) finalize() {
d.pad()
d.keccakF()
}
// squeeze outputs an arbitrary number of bytes from the hash state.
// Squeezing can require multiple calls to the F function (one per rate() bytes squeezed),
// although this is not the case for standard SHA3 parameters. This implementation only supports
// squeezing a single time, subsequent squeezes may lose alignment. Future implementations
// may wish to support multiple squeeze calls, for example to support use as a PRNG.
func (d *digest) squeeze(in []byte, toSqueeze int) []byte {
// Because we read in blocks of laneSize, we need enough room to read
// an integral number of lanes
needed := toSqueeze + (laneSize-toSqueeze%laneSize)%laneSize
if cap(in)-len(in) < needed {
newIn := make([]byte, len(in), len(in)+needed)
copy(newIn, in)
in = newIn
}
out := in[len(in) : len(in)+needed]
for len(out) > 0 {
for i := 0; i < d.rate() && len(out) > 0; i += laneSize {
binary.LittleEndian.PutUint64(out[:], d.a[i/laneSize])
out = out[laneSize:]
}
if len(out) > 0 {
d.keccakF()
}
}
return in[:len(in)+toSqueeze] // Re-slice in case we wrote extra data.
}
// Sum applies padding to the hash state and then squeezes out the desired nubmer of output bytes.
func (d *digest) Sum(in []byte) []byte {
// Make a copy of the original hash so that caller can keep writing and summing.
dup := *d
dup.finalize()
return dup.squeeze(in, dup.outputSize)
}
// The NewKeccakX constructors enable initializing a hash in any of the four recommend sizes
// from the Keccak specification, all of which set capacity=2*outputSize. Note that the final
// NIST standard for SHA3 may specify different input/output lengths.
// The output size is indicated in bits but converted into bytes internally.
func NewKeccak224() hash.Hash { return &digest{outputSize: 224 / 8, capacity: 2 * 224 / 8} }
func NewKeccak256() hash.Hash { return &digest{outputSize: 256 / 8, capacity: 2 * 256 / 8} }
func NewKeccak384() hash.Hash { return &digest{outputSize: 384 / 8, capacity: 2 * 384 / 8} }
func NewKeccak512() hash.Hash { return &digest{outputSize: 512 / 8, capacity: 2 * 512 / 8} }
func Sha3(data ...[]byte) []byte {
d := NewKeccak256()
for _, b := range data {
d.Write(b)
}
return d.Sum(nil)
}

+ 0
- 233
vm/snative.go View File

@ -1,233 +0,0 @@
package vm
import (
"fmt"
. "github.com/tendermint/go-common"
ptypes "github.com/tendermint/tendermint/permission/types"
)
// TODO: ABI
//------------------------------------------------------------------------------------------------
// Registered SNative contracts
func registerSNativeContracts() {
registeredNativeContracts[LeftPadWord256([]byte("has_base"))] = hasBasePerm
registeredNativeContracts[LeftPadWord256([]byte("set_base"))] = setBasePerm
registeredNativeContracts[LeftPadWord256([]byte("unset_base"))] = unsetBasePerm
registeredNativeContracts[LeftPadWord256([]byte("set_global"))] = setGlobalPerm
registeredNativeContracts[LeftPadWord256([]byte("has_role"))] = hasRole
registeredNativeContracts[LeftPadWord256([]byte("add_role"))] = addRole
registeredNativeContracts[LeftPadWord256([]byte("rm_role"))] = rmRole
}
//-----------------------------------------------------------------------------
// snative are native contracts that can access and modify an account's permissions
// TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?)
func hasBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) {
if !HasPermission(appState, caller, ptypes.HasBase) {
return nil, ErrInvalidPermission{caller.Address, "has_base"}
}
if len(args) != 2*32 {
return nil, fmt.Errorf("hasBasePerm() takes two arguments (address, permFlag)")
}
addr, permNum := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr)
if vmAcc == nil {
return nil, fmt.Errorf("Unknown account %X", addr)
}
permN := ptypes.PermFlag(Uint64FromWord256(permNum)) // already shifted
if !ValidPermN(permN) {
return nil, ptypes.ErrInvalidPermission(permN)
}
var permInt byte
if HasPermission(appState, vmAcc, permN) {
permInt = 0x1
} else {
permInt = 0x0
}
dbg.Printf("snative.hasBasePerm(0x%X, %b) = %v\n", addr.Postfix(20), permN, permInt)
return LeftPadWord256([]byte{permInt}).Bytes(), nil
}
func setBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) {
if !HasPermission(appState, caller, ptypes.SetBase) {
return nil, ErrInvalidPermission{caller.Address, "set_base"}
}
if len(args) != 3*32 {
return nil, fmt.Errorf("setBase() takes three arguments (address, permFlag, permission value)")
}
addr, permNum, perm := returnThreeArgs(args)
vmAcc := appState.GetAccount(addr)
if vmAcc == nil {
return nil, fmt.Errorf("Unknown account %X", addr)
}
permN := ptypes.PermFlag(Uint64FromWord256(permNum))
if !ValidPermN(permN) {
return nil, ptypes.ErrInvalidPermission(permN)
}
permV := !perm.IsZero()
if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil {
return nil, err
}
appState.UpdateAccount(vmAcc)
dbg.Printf("snative.setBasePerm(0x%X, %b, %v)\n", addr.Postfix(20), permN, permV)
return perm.Bytes(), nil
}
func unsetBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) {
if !HasPermission(appState, caller, ptypes.UnsetBase) {
return nil, ErrInvalidPermission{caller.Address, "unset_base"}
}
if len(args) != 2*32 {
return nil, fmt.Errorf("unsetBase() takes two arguments (address, permFlag)")
}
addr, permNum := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr)
if vmAcc == nil {
return nil, fmt.Errorf("Unknown account %X", addr)
}
permN := ptypes.PermFlag(Uint64FromWord256(permNum))
if !ValidPermN(permN) {
return nil, ptypes.ErrInvalidPermission(permN)
}
if err = vmAcc.Permissions.Base.Unset(permN); err != nil {
return nil, err
}
appState.UpdateAccount(vmAcc)
dbg.Printf("snative.unsetBasePerm(0x%X, %b)\n", addr.Postfix(20), permN)
return permNum.Bytes(), nil
}
func setGlobalPerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) {
if !HasPermission(appState, caller, ptypes.SetGlobal) {
return nil, ErrInvalidPermission{caller.Address, "set_global"}
}
if len(args) != 2*32 {
return nil, fmt.Errorf("setGlobal() takes two arguments (permFlag, permission value)")
}
permNum, perm := returnTwoArgs(args)
vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256)
if vmAcc == nil {
PanicSanity("cant find the global permissions account")
}
permN := ptypes.PermFlag(Uint64FromWord256(permNum))
if !ValidPermN(permN) {
return nil, ptypes.ErrInvalidPermission(permN)
}
permV := !perm.IsZero()
if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil {
return nil, err
}
appState.UpdateAccount(vmAcc)
dbg.Printf("snative.setGlobalPerm(%b, %v)\n", permN, permV)
return perm.Bytes(), nil
}
func hasRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) {
if !HasPermission(appState, caller, ptypes.HasRole) {
return nil, ErrInvalidPermission{caller.Address, "has_role"}
}
if len(args) != 2*32 {
return nil, fmt.Errorf("hasRole() takes two arguments (address, role)")
}
addr, role := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr)
if vmAcc == nil {
return nil, fmt.Errorf("Unknown account %X", addr)
}
roleS := string(role.Bytes())
var permInt byte
if vmAcc.Permissions.HasRole(roleS) {
permInt = 0x1
} else {
permInt = 0x0
}
dbg.Printf("snative.hasRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0)
return LeftPadWord256([]byte{permInt}).Bytes(), nil
}
func addRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) {
if !HasPermission(appState, caller, ptypes.AddRole) {
return nil, ErrInvalidPermission{caller.Address, "add_role"}
}
if len(args) != 2*32 {
return nil, fmt.Errorf("addRole() takes two arguments (address, role)")
}
addr, role := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr)
if vmAcc == nil {
return nil, fmt.Errorf("Unknown account %X", addr)
}
roleS := string(role.Bytes())
var permInt byte
if vmAcc.Permissions.AddRole(roleS) {
permInt = 0x1
} else {
permInt = 0x0
}
appState.UpdateAccount(vmAcc)
dbg.Printf("snative.addRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0)
return LeftPadWord256([]byte{permInt}).Bytes(), nil
}
func rmRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) {
if !HasPermission(appState, caller, ptypes.RmRole) {
return nil, ErrInvalidPermission{caller.Address, "rm_role"}
}
if len(args) != 2*32 {
return nil, fmt.Errorf("rmRole() takes two arguments (address, role)")
}
addr, role := returnTwoArgs(args)
vmAcc := appState.GetAccount(addr)
if vmAcc == nil {
return nil, fmt.Errorf("Unknown account %X", addr)
}
roleS := string(role.Bytes())
var permInt byte
if vmAcc.Permissions.RmRole(roleS) {
permInt = 0x1
} else {
permInt = 0x0
}
appState.UpdateAccount(vmAcc)
dbg.Printf("snative.rmRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0)
return LeftPadWord256([]byte{permInt}).Bytes(), nil
}
//------------------------------------------------------------------------------------------------
// Errors and utility funcs
type ErrInvalidPermission struct {
Address Word256
SNative string
}
func (e ErrInvalidPermission) Error() string {
return fmt.Sprintf("Account %X does not have permission snative.%s", e.Address.Postfix(20), e.SNative)
}
// Checks if a permission flag is valid (a known base chain or snative permission)
func ValidPermN(n ptypes.PermFlag) bool {
if n > ptypes.TopPermFlag {
return false
}
return true
}
// CONTRACT: length has already been checked
func returnTwoArgs(args []byte) (a Word256, b Word256) {
copy(a[:], args[:32])
copy(b[:], args[32:64])
return
}
// CONTRACT: length has already been checked
func returnThreeArgs(args []byte) (a Word256, b Word256, c Word256) {
copy(a[:], args[:32])
copy(b[:], args[32:64])
copy(c[:], args[64:96])
return
}

+ 0
- 126
vm/stack.go View File

@ -1,126 +0,0 @@
package vm
import (
"fmt"
. "github.com/tendermint/go-common"
)
// Not goroutine safe
type Stack struct {
data []Word256
ptr int
gas *int64
err *error
}
func NewStack(capacity int, gas *int64, err *error) *Stack {
return &Stack{
data: make([]Word256, capacity),
ptr: 0,
gas: gas,
err: err,
}
}
func (st *Stack) useGas(gasToUse int64) {
if *st.gas > gasToUse {
*st.gas -= gasToUse
} else {
st.setErr(ErrInsufficientGas)
}
}
func (st *Stack) setErr(err error) {
if *st.err == nil {
*st.err = err
}
}
func (st *Stack) Push(d Word256) {
st.useGas(GasStackOp)
if st.ptr == cap(st.data) {
st.setErr(ErrDataStackOverflow)
return
}
st.data[st.ptr] = d
st.ptr++
}
// currently only called after Sha3
func (st *Stack) PushBytes(bz []byte) {
if len(bz) != 32 {
PanicSanity("Invalid bytes size: expected 32")
}
st.Push(LeftPadWord256(bz))
}
func (st *Stack) Push64(i int64) {
st.Push(Int64ToWord256(i))
}
func (st *Stack) Pop() Word256 {
st.useGas(GasStackOp)
if st.ptr == 0 {
st.setErr(ErrDataStackUnderflow)
return Zero256
}
st.ptr--
return st.data[st.ptr]
}
func (st *Stack) PopBytes() []byte {
return st.Pop().Bytes()
}
func (st *Stack) Pop64() int64 {
d := st.Pop()
return Int64FromWord256(d)
}
func (st *Stack) Len() int {
return st.ptr
}
func (st *Stack) Swap(n int) {
st.useGas(GasStackOp)
if st.ptr < n {
st.setErr(ErrDataStackUnderflow)
return
}
st.data[st.ptr-n], st.data[st.ptr-1] = st.data[st.ptr-1], st.data[st.ptr-n]
return
}
func (st *Stack) Dup(n int) {
st.useGas(GasStackOp)
if st.ptr < n {
st.setErr(ErrDataStackUnderflow)
return
}
st.Push(st.data[st.ptr-n])
return
}
// Not an opcode, costs no gas.
func (st *Stack) Peek() Word256 {
if st.ptr == 0 {
st.setErr(ErrDataStackUnderflow)
return Zero256
}
return st.data[st.ptr-1]
}
func (st *Stack) Print(n int) {
fmt.Println("### stack ###")
if st.ptr > 0 {
nn := MinInt(n, st.ptr)
for j, i := 0, st.ptr-1; i > st.ptr-1-nn; i-- {
fmt.Printf("%-3d %X\n", j, st.data[i])
j += 1
}
} else {
fmt.Println("-- empty --")
}
fmt.Println("#############")
}

+ 0
- 79
vm/test/fake_app_state.go View File

@ -1,79 +0,0 @@
package vm
import (
. "github.com/tendermint/go-common"
. "github.com/tendermint/tendermint/vm"
"github.com/tendermint/tendermint/vm/sha3"
)
type FakeAppState struct {
accounts map[string]*Account
storage map[string]Word256
}
func (fas *FakeAppState) GetAccount(addr Word256) *Account {
account := fas.accounts[addr.String()]
return account
}
func (fas *FakeAppState) UpdateAccount(account *Account) {
fas.accounts[account.Address.String()] = account
}
func (fas *FakeAppState) RemoveAccount(account *Account) {
_, ok := fas.accounts[account.Address.String()]
if !ok {
panic(Fmt("Invalid account addr: %X", account.Address))
} else {
// Remove account
delete(fas.accounts, account.Address.String())
}
}
func (fas *FakeAppState) CreateAccount(creator *Account) *Account {
addr := createAddress(creator)
account := fas.accounts[addr.String()]
if account == nil {
return &Account{
Address: addr,
Balance: 0,
Code: nil,
Nonce: 0,
}
} else {
panic(Fmt("Invalid account addr: %X", addr))
}
}
func (fas *FakeAppState) GetStorage(addr Word256, key Word256) Word256 {
_, ok := fas.accounts[addr.String()]
if !ok {
panic(Fmt("Invalid account addr: %X", addr))
}
value, ok := fas.storage[addr.String()+key.String()]
if ok {
return value
} else {
return Zero256
}
}
func (fas *FakeAppState) SetStorage(addr Word256, key Word256, value Word256) {
_, ok := fas.accounts[addr.String()]
if !ok {
panic(Fmt("Invalid account addr: %X", addr))
}
fas.storage[addr.String()+key.String()] = value
}
// Creates a 20 byte address and bumps the nonce.
func createAddress(creator *Account) Word256 {
nonce := creator.Nonce
creator.Nonce += 1
temp := make([]byte, 32+8)
copy(temp, creator.Address[:])
PutInt64BE(temp[32:], nonce)
return LeftPadWord256(sha3.Sha3(temp)[:20])
}

+ 0
- 90
vm/test/log_event_test.go View File

@ -1,90 +0,0 @@
package vm
import (
"bytes"
"reflect"
"testing"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/events"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tendermint/vm"
)
var expectedData = []byte{0x10}
var expectedHeight int64 = 0
var expectedTopics = []Word256{
Int64ToWord256(1),
Int64ToWord256(2),
Int64ToWord256(3),
Int64ToWord256(4)}
// Tests logs and events.
func TestLog4(t *testing.T) {
st := newAppState()
// Create accounts
account1 := &Account{
Address: LeftPadWord256(makeBytes(20)),
}
account2 := &Account{
Address: LeftPadWord256(makeBytes(20)),
}
st.accounts[account1.Address.String()] = account1
st.accounts[account2.Address.String()] = account2
ourVm := NewVM(st, newParams(), Zero256, nil)
eventSwitch := events.NewEventSwitch()
_, err := eventSwitch.Start()
if err != nil {
t.Errorf("Failed to start eventSwitch: %v", err)
}
eventID := types.EventStringLogEvent(account2.Address.Postfix(20))
doneChan := make(chan struct{}, 1)
eventSwitch.AddListenerForEvent("test", eventID, func(event types.EventData) {
logEvent := event.(types.EventDataLog)
// No need to test address as this event would not happen if it wasn't correct
if !reflect.DeepEqual(logEvent.Topics, expectedTopics) {
t.Errorf("Event topics are wrong. Got: %v. Expected: %v", logEvent.Topics, expectedTopics)
}
if !bytes.Equal(logEvent.Data, expectedData) {
t.Errorf("Event data is wrong. Got: %s. Expected: %s", logEvent.Data, expectedData)
}
if logEvent.Height != expectedHeight {
t.Errorf("Event block height is wrong. Got: %d. Expected: %d", logEvent.Height, expectedHeight)
}
doneChan <- struct{}{}
})
ourVm.SetFireable(eventSwitch)
var gas int64 = 100000
mstore8 := byte(MSTORE8)
push1 := byte(PUSH1)
log4 := byte(LOG4)
stop := byte(STOP)
code := []byte{
push1, 16, // data value
push1, 0, // memory slot
mstore8,
push1, 4, // topic 4
push1, 3, // topic 3
push1, 2, // topic 2
push1, 1, // topic 1
push1, 1, // size of data
push1, 0, // data starts at this offset
log4,
stop,
}
_, err = ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
<-doneChan
if err != nil {
t.Fatal(err)
}
}

+ 0
- 207
vm/test/vm_test.go View File

@ -1,207 +0,0 @@
package vm
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"testing"
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/events"
ptypes "github.com/tendermint/tendermint/permission/types"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tendermint/vm"
)
func newAppState() *FakeAppState {
fas := &FakeAppState{
accounts: make(map[string]*Account),
storage: make(map[string]Word256),
}
// For default permissions
fas.accounts[ptypes.GlobalPermissionsAddress256.String()] = &Account{
Permissions: ptypes.DefaultAccountPermissions,
}
return fas
}
func newParams() Params {
return Params{
BlockHeight: 0,
BlockHash: Zero256,
BlockTime: 0,
GasLimit: 0,
}
}
func makeBytes(n int) []byte {
b := make([]byte, n)
rand.Read(b)
return b
}
// Runs a basic loop
func TestVM(t *testing.T) {
ourVm := NewVM(newAppState(), newParams(), Zero256, nil)
// Create accounts
account1 := &Account{
Address: Int64ToWord256(100),
}
account2 := &Account{
Address: Int64ToWord256(101),
}
var gas int64 = 100000
N := []byte{0x0f, 0x0f}
// Loop N times
code := []byte{0x60, 0x00, 0x60, 0x20, 0x52, 0x5B, byte(0x60 + len(N) - 1)}
code = append(code, N...)
code = append(code, []byte{0x60, 0x20, 0x51, 0x12, 0x15, 0x60, byte(0x1b + len(N)), 0x57, 0x60, 0x01, 0x60, 0x20, 0x51, 0x01, 0x60, 0x20, 0x52, 0x60, 0x05, 0x56, 0x5B}...)
start := time.Now()
output, err := ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
fmt.Printf("Output: %v Error: %v\n", output, err)
fmt.Println("Call took:", time.Since(start))
if err != nil {
t.Fatal(err)
}
}
// Tests the code for a subcurrency contract compiled by serpent
func TestSubcurrency(t *testing.T) {
st := newAppState()
// Create accounts
account1 := &Account{
Address: LeftPadWord256(makeBytes(20)),
}
account2 := &Account{
Address: LeftPadWord256(makeBytes(20)),
}
st.accounts[account1.Address.String()] = account1
st.accounts[account2.Address.String()] = account2
ourVm := NewVM(st, newParams(), Zero256, nil)
var gas int64 = 1000
code_parts := []string{"620f42403355",
"7c0100000000000000000000000000000000000000000000000000000000",
"600035046315cf268481141561004657",
"6004356040526040515460605260206060f35b63693200ce81141561008757",
"60043560805260243560a052335460c0523360e05260a05160c05112151561008657",
"60a05160c0510360e0515560a0516080515401608051555b5b505b6000f3"}
code, _ := hex.DecodeString(strings.Join(code_parts, ""))
fmt.Printf("Code: %x\n", code)
data, _ := hex.DecodeString("693200CE0000000000000000000000004B4363CDE27C2EB05E66357DB05BC5C88F850C1A0000000000000000000000000000000000000000000000000000000000000005")
output, err := ourVm.Call(account1, account2, code, data, 0, &gas)
fmt.Printf("Output: %v Error: %v\n", output, err)
if err != nil {
t.Fatal(err)
}
}
// Test sending tokens from a contract to another account
func TestSendCall(t *testing.T) {
fakeAppState := newAppState()
ourVm := NewVM(fakeAppState, newParams(), Zero256, nil)
// Create accounts
account1 := &Account{
Address: Int64ToWord256(100),
}
account2 := &Account{
Address: Int64ToWord256(101),
}
account3 := &Account{
Address: Int64ToWord256(102),
}
// account1 will call account2 which will trigger CALL opcode to account3
addr := account3.Address.Postfix(20)
contractCode := callContractCode(addr)
//----------------------------------------------
// account2 has insufficient balance, should fail
fmt.Println("Should fail with insufficient balance")
exception := runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
if exception == "" {
t.Fatal("Expected exception")
}
//----------------------------------------------
// give account2 sufficient balance, should pass
account2.Balance = 100000
exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
if exception != "" {
t.Fatal("Unexpected exception", exception)
}
//----------------------------------------------
// insufficient gas, should fail
fmt.Println("Should fail with insufficient gas")
account2.Balance = 100000
exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 100)
if exception == "" {
t.Fatal("Expected exception")
}
}
// subscribes to an AccCall, runs the vm, returns the exception
func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas int64) string {
// we need to catch the event from the CALL to check for exceptions
evsw := events.NewEventSwitch()
evsw.Start()
ch := make(chan interface{})
fmt.Printf("subscribe to %x\n", subscribeAddr)
evsw.AddListenerForEvent("test", types.EventStringAccCall(subscribeAddr), func(msg types.EventData) {
ch <- msg
})
evc := events.NewEventCache(evsw)
ourVm.SetFireable(evc)
go func() {
start := time.Now()
output, err := ourVm.Call(caller, callee, contractCode, []byte{}, 0, &gas)
fmt.Printf("Output: %v Error: %v\n", output, err)
fmt.Println("Call took:", time.Since(start))
if err != nil {
ch <- err.Error()
}
evc.Flush()
}()
msg := <-ch
switch ev := msg.(type) {
case types.EventDataTx:
return ev.Exception
case types.EventDataCall:
return ev.Exception
case string:
return ev
}
return ""
}
// this is code to call another contract (hardcoded as addr)
func callContractCode(addr []byte) []byte {
gas1, gas2 := byte(0x1), byte(0x1)
value := byte(0x69)
inOff, inSize := byte(0x0), byte(0x0) // no call data
retOff, retSize := byte(0x0), byte(0x20)
// this is the code we want to run (send funds to an account and return)
contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73}
contractCode = append(contractCode, addr...)
contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...)
return contractCode
}
/*
// infinite loop
code := []byte{0x5B, 0x60, 0x00, 0x56}
// mstore
code := []byte{0x60, 0x00, 0x60, 0x20}
// mstore, mload
code := []byte{0x60, 0x01, 0x60, 0x20, 0x52, 0x60, 0x20, 0x51}
*/

+ 0
- 49
vm/types.go View File

@ -1,49 +0,0 @@
package vm
import (
. "github.com/tendermint/go-common"
ptypes "github.com/tendermint/tendermint/permission/types"
)
const (
defaultDataStackCapacity = 10
)
type Account struct {
Address Word256
Balance int64
Code []byte
Nonce int64
Other interface{} // For holding all other data.
Permissions ptypes.AccountPermissions
}
func (acc *Account) String() string {
if acc == nil {
return "nil-VMAccount"
}
return Fmt("VMAccount{%X B:%v C:%X N:%v}",
acc.Address, acc.Balance, acc.Code, acc.Nonce)
}
type AppState interface {
// Accounts
GetAccount(addr Word256) *Account
UpdateAccount(*Account)
RemoveAccount(*Account)
CreateAccount(*Account) *Account
// Storage
GetStorage(Word256, Word256) Word256
SetStorage(Word256, Word256, Word256) // Setting to Zero is deleting.
}
type Params struct {
BlockHeight int64
BlockHash Word256
BlockTime int64
GasLimit int64
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save